-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor SemanticCreator #16700
Refactor SemanticCreator #16700
Changes from 3 commits
82c10e8
15b2bb6
ecdeee1
5dd3090
a27894f
8b72e9e
44824b1
3b834d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
package org.apache.druid.common.semantic; | ||
|
||
import org.apache.druid.error.DruidException; | ||
|
||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
import java.util.HashMap; | ||
import java.util.LinkedHashMap; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
|
||
public class SemanticUtils | ||
{ | ||
private static final Map<Class<?>, Map<Class<?>, Function<?, ?>>> OVERRIDES = new LinkedHashMap<>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need to put everything into a centralized map? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This exists to allow extensions to register new interfaces and implementations without needing to impact the core code. This is probably worthy of javadoc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a javadoc There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you mean to override default behaviour? what's the problem you are trying to solve? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Essentially, we could have a class A that has a SemanticCreator toB(). This allows A.as(B.class) to work. However, there could be extensions that want a different implementation to be bound. The extension would want to use that implementation only if the extension is loaded and also, it would not be able to change the first binding. Overrides will allow the extension to bind something like:
to modify |
||
|
||
/** | ||
* Allows the registration of overrides, which allows overriding of already existing mappings. | ||
* This allows extensions to register mappings. | ||
*/ | ||
@SuppressWarnings("unused") | ||
public static <C, T> void registerAsOverride(Class<C> clazz, Class<T> asInterface, Function<C, T> fn) | ||
adarshsanjeev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
final Map<Class<?>, Function<?, ?>> classOverrides = OVERRIDES.computeIfAbsent( | ||
clazz, | ||
theClazz -> new LinkedHashMap<>() | ||
); | ||
|
||
final Function<?, ?> oldVal = classOverrides.get(asInterface); | ||
if (oldVal != null) { | ||
throw DruidException.defensive( | ||
"Attempt to side-override the same interface [%s] multiple times for the same class [%s].", | ||
asInterface, | ||
clazz | ||
); | ||
} else { | ||
classOverrides.put(asInterface, fn); | ||
} | ||
} | ||
|
||
public static <T> Map<Class<?>, Function<T, ?>> makeAsMap(Class<T> clazz) | ||
{ | ||
final Map<Class<?>, Function<T, ?>> retVal = new HashMap<>(); | ||
|
||
for (Method method : clazz.getMethods()) { | ||
if (method.isAnnotationPresent(SemanticCreator.class)) { | ||
if (method.getParameterCount() != 0) { | ||
throw DruidException.defensive("Method [%s] annotated with SemanticCreator was not 0-argument.", method); | ||
} | ||
|
||
retVal.put(method.getReturnType(), arg -> { | ||
try { | ||
return method.invoke(arg); | ||
} | ||
catch (InvocationTargetException | IllegalAccessException e) { | ||
throw DruidException.defensive().build(e, "Problem invoking method [%s]", method); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
final Map<Class<?>, Function<?, ?>> classOverrides = OVERRIDES.get(clazz); | ||
if (classOverrides != null) { | ||
for (Map.Entry<Class<?>, Function<?, ?>> overrideEntry : classOverrides.entrySet()) { | ||
//noinspection unchecked | ||
retVal.put(overrideEntry.getKey(), (Function<T, ?>) overrideEntry.getValue()); | ||
} | ||
} | ||
|
||
return retVal; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
package org.apache.druid.query.rowsandcols.semantic; | ||
|
||
import org.apache.druid.frame.Frame; | ||
import org.apache.druid.frame.allocation.ArenaMemoryAllocatorFactory; | ||
import org.apache.druid.frame.write.FrameWriter; | ||
import org.apache.druid.frame.write.FrameWriters; | ||
import org.apache.druid.query.rowsandcols.RowsAndColumns; | ||
import org.apache.druid.query.rowsandcols.column.Column; | ||
import org.apache.druid.segment.ColumnSelectorFactory; | ||
import org.apache.druid.segment.column.RowSignature; | ||
|
||
import java.util.Collections; | ||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
public class DefaultFrameMaker implements FrameMaker | ||
{ | ||
private final RowsAndColumns rac; | ||
|
||
public DefaultFrameMaker(RowsAndColumns rac) | ||
{ | ||
this.rac = rac; | ||
} | ||
|
||
@Override | ||
public RowSignature computeSignature() | ||
{ | ||
final RowSignature.Builder signatureBuilder = RowSignature.builder(); | ||
for (String column : rac.getColumnNames()) { | ||
final Column racColumn = rac.findColumn(column); | ||
if (racColumn == null) { | ||
continue; | ||
} | ||
signatureBuilder.add(column, racColumn.toAccessor().getType()); | ||
} | ||
|
||
return signatureBuilder.build(); | ||
} | ||
|
||
@Override | ||
public Frame toColumnBasedFrame() | ||
{ | ||
final AtomicInteger rowId = new AtomicInteger(0); | ||
final int numRows = rac.numRows(); | ||
final ColumnSelectorFactoryMaker csfm = ColumnSelectorFactoryMaker.fromRAC(rac); | ||
final ColumnSelectorFactory selectorFactory = csfm.make(rowId); | ||
|
||
final ArenaMemoryAllocatorFactory memFactory = new ArenaMemoryAllocatorFactory(200 << 20); // 200 MB | ||
|
||
final FrameWriter frameWriter = FrameWriters.makeColumnBasedFrameWriterFactory( | ||
memFactory, | ||
computeSignature(), | ||
Collections.emptyList() | ||
).newFrameWriter(selectorFactory); | ||
|
||
rowId.set(0); | ||
for (; rowId.get() < numRows; rowId.incrementAndGet()) { | ||
frameWriter.addSelection(); | ||
} | ||
|
||
return Frame.wrap(frameWriter.toByteArray()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this
Utils
could be renamed to be the interface containing theas
for these things; thestatic
method could remain as those are providing services...please also add apidoc how this
as
stuff works/etcThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea about creating the interface for
.as()
and having these utils be static helpers on it. As a refactor, that should probably be done for all of the places that are using an.as()
so I think I'd like to let this current PR go through (as it's following the current pattern in the code) and one of us can take on the refactor as you defined it as I do think that's an improvement.