Add @ExtensionPoint and @PublicApi annotations.#4433
Conversation
|
Is druid-api module still needed? |
|
Maybe not. The purpose was to put everything that was a public API in one package, but it turns out that there are public APIs in other packages too. |
|
I cleaned up the wording a bit, to clarify that PublicApis can have new non-default methods added to an interface and this is not considered a breakage (because they are not meant to be subclassed). |
|
👍 |
| * | ||
| * @see PublicApi | ||
| */ | ||
| @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR}) |
There was a problem hiding this comment.
How could a field, method or constructor be an extension point?
There was a problem hiding this comment.
It can't, this is a copy/paste error. I'll remove those.
| /** | ||
| */ | ||
| @PublicApi | ||
| public class Runnables |
There was a problem hiding this comment.
Such utility classes are not generally in API, they are in druid-common
There was a problem hiding this comment.
This one is probably here since it's used by FileIteratingFirehose which is in druid-api.
| /** | ||
| */ | ||
| @PublicApi | ||
| public class Queries |
There was a problem hiding this comment.
Should it really be public API?
There was a problem hiding this comment.
It's utility methods that are use in the implementations of most built-in queries and post-aggregators. If it's not a public api, then people that implement their own extension based on the built-in code (which is probably common) will accidentally use internal apis.
There was a problem hiding this comment.
There's a judgement call here: extension implementors could always copy/paste the code from Queries rather than linking to it directly, and there is cost to the core project for having too many public apis. But I think that in this case it's worth it.
| @Nullable | ||
| public ColumnCapabilities getColumnCapabilities(String column); | ||
|
|
||
| public Map<String, DimensionHandler> getDimensionHandlers(); |
There was a problem hiding this comment.
It was unused, and I didn't want it to be part of the public API of StorageAdapter.
| this.columnSize = columnSize; | ||
| } | ||
|
|
||
| @PublicApi |
There was a problem hiding this comment.
Since it's commonly used in implementations of ComplexMetricsSerde, which is an extension point.
|
Pushed a new commit to resolve conflicts and address #4433 (comment). |
|
@gianm I have heard from @cheddar before that the json is the api spec rather than the java code itself per-se. Since there is a mix of things which feed into jackson and things which don't feed into jackson that have the same annotation, would it make sense to have a special annotation for things which are part of the JSON api, rather than the Java api? |
| import java.util.List; | ||
|
|
||
| /** | ||
| * Not an {@code ExtensionPoint} since extension parsers are meant to extend {@code InputRowParser}. |
There was a problem hiding this comment.
given that InputRowParser is an extension point, this needs to be an extension point or else how would they implement InputRowParser.getParseSpec() ?
There was a problem hiding this comment.
That's a good point…
I guess ParseSpec should be an extension point? And that would mean TimestampSpec and DImensionsSpec should be public apis.
I'll change it.
There was a problem hiding this comment.
Added that in the most recent commit.
| /** | ||
| */ | ||
| @PublicApi | ||
| public class StringInputRowParser implements ByteBufferInputRowParser |
There was a problem hiding this comment.
do we expect extensions to directly reference StringInputRowParser ?
There was a problem hiding this comment.
I was thinking it would be useful for people that want to implement extension firehoses that parse strings. Otherwise they'll need to duplicate a lot of logic.
There was a problem hiding this comment.
Maybe nobody really uses it though and we can take out the annotation. Hmm, ok I'll take it out.
|
|
||
| /** | ||
| */ | ||
| @PublicApi |
There was a problem hiding this comment.
actually its more of an utility class and shouldn't necessarily be directly used in custom user extension (it can be used in out-of-the-box extensions) and those should really just use MapBinder.xxx .
There was a problem hiding this comment.
I was thinking it would be nice to make a utility like this public. I don't feel super strongly though.
For this I think the attitude has been (and can continue to be) that a JSON API is stable if, and only if, it is documented. So I don't think an annotation is necessary. |
|
It was brought up on the dev sync that it would be desirable to merge java-util into druid-common, which would solve the problem of "java-util can't see the annotations". That would be a big patch though and I think I'd like to do this patch first, for everything other than java-util. And then do another one for merging in java-util to druid-common. |
| */ | ||
| public interface | ||
| InputRow extends Row | ||
| @PublicApi |
There was a problem hiding this comment.
shouldn't this be an extension point instead ? InputRowParser is an extension point and parse(..) should be free to return a concrete implementation that it wants.
There was a problem hiding this comment.
I was thinking they would all use MapBasedRow, since the current parsers do that, rather than implement their own Row subclasses. We can make it an extension point if you think that makes more sense though.
There was a problem hiding this comment.
hmmm, so I took a brief look at avro and orc parsers which create MapBasedInputRow and it looks like it would be better to create an impl of InputRow instead .
to use MapBasedInputRow..
-
avro parser makes GenericRecord look like a Map here ( https://github.com/druid-io/druid/blob/master/extensions-core/avro-extensions/src/main/java/io/druid/data/input/avro/GenericRecordAsMap.java ) and most methods have "unimplemented exceptions" , would probably be better to just make GenericRecord look like an InputRow.
-
orc parser converts its ord record into a map here ( https://github.com/druid-io/druid/blob/master/extensions-contrib/orc-extensions/src/main/java/io/druid/data/input/orc/OrcHadoopInputRowParser.java#L78 ) which would be inefficient if your input record had alot of columns that were not being indexed into druid. again it would be better to make orc record look like an InputRow instead.
with that information, what do you think ?
There was a problem hiding this comment.
Hmm, thanks for looking into that. In that case I think it makes sense to make this an extension point. I'll change it.
|
|
||
| /** | ||
| */ | ||
| @PublicApi |
There was a problem hiding this comment.
is this public api so that users can use this class in their java clients to construct druid queries ?
There was a problem hiding this comment.
It's because ParseSpec is an extension point, and it returns DimensionsSpec, which is constructed using DimensionSchema. So implementors of custom ParseSpecs might need to use this class.
| import io.druid.guice.annotations.PublicApi; | ||
| import org.joda.time.Interval; | ||
|
|
||
| @PublicApi |
There was a problem hiding this comment.
QueryToolChest.filterSegments(..) returns an impl of this , so this needs to be an extension point.
There was a problem hiding this comment.
It's supposed to return the same objects that it was given (but possibly filtered). So it doesn't need to be an extension point (people won't subclass it), just a public api (people may need to call methods on it).
|
@gianm is We should also mark |
|
@gianm |
I consciously didn't add Task because it's not listed on http://druid.io/docs/latest/development/modules.html. And with this PR, I was more trying to encode existing policy in annotations, rather than change the policy. That being said, I would +1 a proposal to make Task an extension point.
Similar to Task, I left it out since it's not on http://druid.io/docs/latest/development/modules.html. In addition, Lookup APIs are explicitly described as "experimental" in the docs, so I think they shouldn't be annotated as stable. As long as they're experimental, users should be free to make extensions for lookups, but they shouldn't be guaranteed to keep working across upgrades.
Hmm, ServletFilterHolder is kind of borderline (it's not on http://druid.io/docs/latest/development/modules.html) but it's close enough to "Jersey resources" (which is) that I guess it makes sense to add. I'll do that. PasswordProvider isn't on http://druid.io/docs/latest/development/modules.html but maybe it should be. I'll leave the annotation out for now. |
|
What do people think about adding these as extension points?
They make sense to me but since they aren't currently on the modules.md list I wanted to check for opinions. If we generally think they make sense then I can add them to modules.md and annotate them. |
|
@gianm @himanshug it makes sense to me. |
|
@gianm |
I'll make PasswordProvider an extension point too.
I'll leave Task as "internal" for now, but if there is some consensus that it's a good idea to make it a formal extension point, then I'm ok with changing it. |
|
@himanshug @jihoonson @leventov I made the adjustments discussed, and also updated the modules.md doc to reflect what has been annotated (as well as provided some more "hints" for implementors). Please let me know what you think, especially about the modules.md changes. |
|
👍 |
|
There have been 3 approvals here, it should be good to go after conflicts are resolved. |
|
Resolved conflicts. |
|
@gianm feel free to merge whenever you want. it has enough approvals. |
|
Ok, I'll merge it after CI is done. |
This is an attempt to clarify questions about what is and is not a public API (see #4131 (comment), #4394 (comment), #4254 (comment)). It adds two new annotations:
@ExtensionPointwhich signifies something you can subclass, and@PublicApiwhich signifies something you're not meant to subclass, but that you can use for implementation. The idea is that if an extension author sticks to public APIs then their extension will keep working for at least an entire major Druid line.Extension authors are of course free to use non-public APIs, but their extensions might break randomly.
Feedback on the specific list of annotated and non-annotated items is welcome.
Some notes:
GenericQueryMetricsFactoryas public (since AFAIK any extension query would need to call methods on it) but nothing else in the query metrics world.