Future-proof some Guava usage#5414
Conversation
* Use a java-util EmptyIterator instead of Guava's * Change some of the guava future handling to do manual async transforms. Guava changes transform into transformAsync by deprecating transform in ONLY Guava 19. Then its gone in 20
| return new Firehose() | ||
| { | ||
| private Iterator<InputRow> nextIterator = Iterators.emptyIterator(); | ||
| private Iterator<InputRow> nextIterator = EmptyIterator.instance(); |
There was a problem hiding this comment.
Why not Collections.emptyIterator()?
| "dataSegment for segmentId[%s]", | ||
| segmentWithState.getSegmentIdentifier()) | ||
| segmentWithState.getSegmentIdentifier() | ||
| ) |
| committerSupplier.get(), | ||
| Collections.singletonList(sequenceName) | ||
| ); | ||
| final SettableFuture<SegmentsAndMetadata> handoffDoneFuture = SettableFuture.create(); |
There was a problem hiding this comment.
Could this boilerplate be extracted in some method?
There was a problem hiding this comment.
in theory yes, but the different places this kind of style is used have slightly different usage patterns, and I didn't want to spend the mental bandwidth balancing between java futures and guava futures (that are also viable across a wide swath of Guava version) that also fit the different use cases. So I just wrote things specific for each use case.
| pushInBackground(null, segmentIdentifierList), | ||
| this::dropInBackground | ||
| ); | ||
| final CompletableFuture<SegmentsAndMetadata> chainEndFuture = new CompletableFuture<>(); |
There was a problem hiding this comment.
If SettableFuture is used instead of CompletableFuture, this code becomes isomorphic to the code in AppenderatorDriverRealtimeIndexTask and a common method transform() (replacement of Futures.transform()) could be extracted.
| <artifactId>guice-multibindings</artifactId> | ||
| <version>${guice.version}</version> | ||
| </dependency> | ||
| <dependency> |
There was a problem hiding this comment.
What is this? Why this is needed?
There was a problem hiding this comment.
Future versions of guava fail to pull this in. 19 specifically if I recall. This causes builds to fail for guava 19 without it.
| pushInBackground(wrapCommitter(committer), theSegments), | ||
| (AsyncFunction<SegmentsAndMetadata, SegmentsAndMetadata>) segmentsAndMetadata -> publishInBackground( | ||
| segmentsAndMetadata, | ||
| final SettableFuture<SegmentsAndMetadata> publishCompleted = SettableFuture.create(); |
| publish(publisher, committer, sequenceNames), | ||
| this::registerHandoff | ||
| ); | ||
| final SettableFuture<SegmentsAndMetadata> publishDoneFuture = SettableFuture.create(); |
|
@leventov addressed comments |
| this::registerHandoff | ||
| ); | ||
| final SettableFuture<SegmentsAndMetadata> publishDoneFuture = SettableFuture.create(); | ||
| final ListenableFuture<SegmentsAndMetadata> publishFuture = publish(publisher, committer, sequenceNames); |
There was a problem hiding this comment.
Unless there is a bug, as it is currently written, there is no point in creation of publishDoneFuture and publishFuture could be just returned from this method.
| pushInBackground(null, segmentIdentifierList), | ||
| this::dropInBackground | ||
| ); | ||
| final CompletableFuture<SegmentsAndMetadata> chainEndFuture = new CompletableFuture<>(); |
There was a problem hiding this comment.
If SettableFuture is used instead of CompletableFuture, this code becomes isomorphic to the code in AppenderatorDriverRealtimeIndexTask and a common method transform() (replacement of Futures.transform()) could be extracted.
|
@leventov I thiiiiiink I put in something like what you are asking for. Let me know if this is close |
|
@drcrallen please respond to this comment - #5414 (review) |
|
I dis but pushed to the wrong branch. Fixing. |
| @@ -0,0 +1 @@ | |||
| com.google.common.util.concurrent.Futures.transform(com.google.common.util.concurrent.ListenableFuture<I>, com.google.common.util.concurrent.AsyncFunction<? super I,? extends O>) @ Use io.druid.java.util.common.concurrent.ListenableFutures.transform No newline at end of file | |||
There was a problem hiding this comment.
I don't think it needs to be a different file, druid-forbidden-apis.txt already forbids Guava APIs.
There was a problem hiding this comment.
Forbid Iterators.emptyIterator()
There was a problem hiding this comment.
And still -
I don't actually see Futures.transform() is gone. It seems to me that it's deprecated, but not gone.
And won't be removed, if Guava keeps the current policy of non-removing APIs.
There was a problem hiding this comment.
The async one is gone, or at least changed to a new method name, I think asyncTransform. The method for the non-async one is still there and unchanged.
There was a problem hiding this comment.
I see now. Could you please
- Match the method name in ListenableFutures with the present name in Guava library.
- Add comments to the method, and to the entry in forbidden-apis file (it supports comments with
#), that they should be removed, with switch back to Guava, as soon as the minimum supported Guava version becomes 19.0.
| subHashTable2Buffer = subHashTable2Buffer.slice(); | ||
|
|
||
| subHashTableBuffers = new ByteBuffer[] {subHashTable1Buffer, subHashTable2Buffer}; | ||
| subHashTableBuffers = new ByteBuffer[]{subHashTable1Buffer, subHashTable2Buffer}; |
There was a problem hiding this comment.
Could you add a checkstyle rule (I think it could be a simple regex) to catch such things automatically?
There was a problem hiding this comment.
Outside scope of this PR
| * <ul> | ||
| * <li>null, boolean, int, long, float, double, string, Records, Enums, Maps, Fixed -> String, using String.valueOf</li> | ||
| * <li>bytes -> Arrays.toString() or new String if binaryAsString is true</li> | ||
| * <li>Arrays -> List<String>, using Lists.transform(<List>dimValue, TO_STRING_INCLUDING_NULL)</li> |
There was a problem hiding this comment.
It shouldn't be changed, as well as all other comments, changed in this commit.
There was a problem hiding this comment.
sigh intellij... fixed
| /** | ||
| * Guava 19 changes the Futures.transform signature so that the async form is different. This is here as a | ||
| * compatability layer until such a time as druid only supports Guava 19 or later, in which case | ||
| * Futrues.transformAsync should be used |
| com.google.common.collect.MapMaker @ Create java.util.concurrent.ConcurrentHashMap directly | ||
| com.google.common.collect.Maps#newConcurrentMap() @ Create java.util.concurrent.ConcurrentHashMap directly No newline at end of file | ||
| com.google.common.collect.Maps#newConcurrentMap() @ Create java.util.concurrent.ConcurrentHashMap directly | ||
| com.google.common.util.concurrent.Futures.transform(com.google.common.util.concurrent.ListenableFuture<I>, com.google.common.util.concurrent.AsyncFunction<? super I,? extends O>) @ Use io.druid.java.util.common.concurrent.ListenableFutures.transformAsync |
There was a problem hiding this comment.
Also forbid Iterators.emptyIterator()
| com.google.common.collect.MapMaker @ Create java.util.concurrent.ConcurrentHashMap directly | ||
| com.google.common.collect.Maps#newConcurrentMap() @ Create java.util.concurrent.ConcurrentHashMap directly | ||
| com.google.common.util.concurrent.Futures.transform(com.google.common.util.concurrent.ListenableFuture<I>, com.google.common.util.concurrent.AsyncFunction<? super I,? extends O>) @ Use io.druid.java.util.common.concurrent.ListenableFutures.transformAsync | ||
| com.google.common.util.concurrent.Futures.transform(com.google.common.util.concurrent.ListenableFuture<I>, com.google.common.util.concurrent.AsyncFunction<? super I,? extends O>) @ Use io.druid.java.util.common.concurrent.ListenableFutures#transformAsync |
There was a problem hiding this comment.
This line uses different method referencing syntax than the other lines (Futures.transform vs. Maps#newConcurrentMap). Did you check that this prohibition works by trying to add a use of prohibited method to code and then mvn install to see if the build fails?
|
@leventov any other comments here? |
| public class ListenableFutures | ||
| { | ||
| /** | ||
| * Guava 19 changes the Futures.transform signature so that the async form is different. This is here as a |
There was a problem hiding this comment.
Is this code copied from Guava? If so, the comment should say so. If not, the comment should also say so, since people might think it was anyway.
| this::registerHandoff | ||
| ); | ||
| return ListenableFutures | ||
| .transformAsync( |
There was a problem hiding this comment.
this line break doesn't seem necessary. It is a small thing but I still feel compelled to mention it.
| this::dropInBackground | ||
| ); | ||
| final ListenableFuture<SegmentsAndMetadata> future = ListenableFutures | ||
| .transformAsync( |
There was a problem hiding this comment.
this line break doesn't seem necessary. It is a small thing but I still feel compelled to mention it.
| /** | ||
| * This class is specialized for streaming ingestion. In streaming ingestion, the segment lifecycle is like: | ||
| * | ||
| * <p> |
There was a problem hiding this comment.
I think we avoid these <p> tags since the linter doesn't like them. Try turning off "Generate p on empty lines" in the JavaDoc section of your code style pane.
| synchronized (segments) { | ||
| sequenceNames.forEach(segments::remove); | ||
| } | ||
| final SettableFuture<SegmentsAndMetadata> future = SettableFuture.create(); |
There was a problem hiding this comment.
Why change this to an async transform that generates a useless future, instead of keeping it as a normal transform?
There was a problem hiding this comment.
I'm not following, what do you mean by a "normal transform" here?
There was a problem hiding this comment.
I think I fixed this, but please check
There was a problem hiding this comment.
By "normal transform" I meant one that uses a regular Function rather than AsyncFunction. The new diff looks good to me now.
| com.google.common.collect.MapMaker @ Create java.util.concurrent.ConcurrentHashMap directly | ||
| com.google.common.collect.Maps#newConcurrentMap() @ Create java.util.concurrent.ConcurrentHashMap directly No newline at end of file | ||
| com.google.common.collect.Maps#newConcurrentMap() @ Create java.util.concurrent.ConcurrentHashMap directly | ||
| com.google.common.util.concurrent.Futures.transform(com.google.common.util.concurrent.ListenableFuture<I>, com.google.common.util.concurrent.AsyncFunction<? super I,? extends O>) @ Use io.druid.java.util.common.concurrent.ListenableFutures#transformAsync |
There was a problem hiding this comment.
It's still different from other signatures. I'm not sure which syntax is correct, so please check that those prohibitions work
There was a problem hiding this comment.
https://github.com/policeman-tools/forbidden-apis/wiki/SignaturesSyntax I'm trying this out more to see.
If the signature is this
com.google.common.util.concurrent.Futures#transform(com.google.common.util.concurrent.ListenableFuture, com.google.common.util.concurrent.AsyncFunction) @ Use io.druid.java.util.common.concurrent.ListenableFutures#transformAsync
And I change one of the lines to this
return Futures.transform(
publishFuture,
(com.google.common.util.concurrent.AsyncFunction<? super SegmentsAndMetadata, ? extends SegmentsAndMetadata>) sam -> {
//(Function<? super SegmentsAndMetadata, ? extends SegmentsAndMetadata>) sam -> {
synchronized (segments) {
sequenceNames.forEach(segments::remove);
}
final SettableFuture<SegmentsAndMetadata> samfuture = SettableFuture.create();
samfuture.set(sam);
return samfuture;
}
);then maven gives me this:
[ERROR] Forbidden method invocation: com.google.common.util.concurrent.Futures#transform(com.google.common.util.concurrent.ListenableFuture, com.google.common.util.concurrent.AsyncFunction) [Use io.druid.java.util.common.concurrent.ListenableFutures#transformAsync]
[ERROR] in io.druid.segment.realtime.appenderator.StreamAppenderatorDriver (StreamAppenderatorDriver.java:278)
[ERROR] Scanned 1005 (and 1148 related) class file(s) for forbidden API invocations (in 0.63s), 1 error(s).
So I'll make a change here that is validated to work. I could have sworn I tried the one in this comment before, but apparently not. Now I have a validated one though.
|
Thanks! |
Collections.emptyIterator()instead of Guava'stransforms. Guava changes transform into transformAsync by deprecating
transform in ONLY Guava 19. Then its gone in 20