Skip to content

Future-proof some Guava usage#5414

Merged
gianm merged 16 commits intoapache:masterfrom
drcrallen:guava/lessGuavaCompatProblems
Mar 20, 2018
Merged

Future-proof some Guava usage#5414
gianm merged 16 commits intoapache:masterfrom
drcrallen:guava/lessGuavaCompatProblems

Conversation

@drcrallen
Copy link
Copy Markdown
Contributor

@drcrallen drcrallen commented Feb 22, 2018

  • Use a Collections.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

* 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();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not Collections.emptyIterator()?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hah, doh. Fixed

"dataSegment for segmentId[%s]",
segmentWithState.getSegmentIdentifier())
segmentWithState.getSegmentIdentifier()
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bad formatting

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty now!

committerSupplier.get(),
Collections.singletonList(sequenceName)
);
final SettableFuture<SegmentsAndMetadata> handoffDoneFuture = SettableFuture.create();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this boilerplate be extracted in some method?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I filed #5415

pushInBackground(null, segmentIdentifierList),
this::dropInBackground
);
final CompletableFuture<SegmentsAndMetadata> chainEndFuture = new CompletableFuture<>();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread pom.xml
<artifactId>guice-multibindings</artifactId>
<version>${guice.version}</version>
</dependency>
<dependency>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this? Why this is needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here

publish(publisher, committer, sequenceNames),
this::registerHandoff
);
final SettableFuture<SegmentsAndMetadata> publishDoneFuture = SettableFuture.create();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here

@drcrallen
Copy link
Copy Markdown
Contributor Author

@leventov addressed comments

Copy link
Copy Markdown
Member

@leventov leventov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • I don't actually see Futures.transform() is gone. It seems to me that it's deprecated, but not gone.

  • Please prohibit methods that you avoid using forbidden-apis, as @b-slim suggested in the other PR.

this::registerHandoff
);
final SettableFuture<SegmentsAndMetadata> publishDoneFuture = SettableFuture.create();
final ListenableFuture<SegmentsAndMetadata> publishFuture = publish(publisher, committer, sequenceNames);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug, fixing

pushInBackground(null, segmentIdentifierList),
this::dropInBackground
);
final CompletableFuture<SegmentsAndMetadata> chainEndFuture = new CompletableFuture<>();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@drcrallen
Copy link
Copy Markdown
Contributor Author

@leventov I thiiiiiink I put in something like what you are asking for. Let me know if this is close

@leventov
Copy link
Copy Markdown
Member

leventov commented Feb 23, 2018

@drcrallen please respond to this comment - #5414 (review)

@drcrallen
Copy link
Copy Markdown
Contributor Author

I dis but pushed to the wrong branch. Fixing.

@drcrallen
Copy link
Copy Markdown
Contributor Author

@leventov 7e34fd3 is in the correct branch now

Comment thread codestyle/guava-forbidden-apis.txt Outdated
@@ -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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it needs to be a different file, druid-forbidden-apis.txt already forbids Guava APIs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forbid Iterators.emptyIterator()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

subHashTable2Buffer = subHashTable2Buffer.slice();

subHashTableBuffers = new ByteBuffer[] {subHashTable1Buffer, subHashTable2Buffer};
subHashTableBuffers = new ByteBuffer[]{subHashTable1Buffer, subHashTable2Buffer};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a checkstyle rule (I think it could be a simple regex) to catch such things automatically?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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&lt;String&gt;, using Lists.transform(&lt;List&gt;dimValue, TO_STRING_INCLUDING_NULL)</li>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't be changed, as well as all other comments, changed in this commit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment thread codestyle/druid-forbidden-apis.txt Outdated
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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also forbid Iterators.emptyIterator()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pushed

Comment thread codestyle/druid-forbidden-apis.txt Outdated
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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@drcrallen
Copy link
Copy Markdown
Contributor Author

@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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

this::registerHandoff
);
return ListenableFutures
.transformAsync(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line break doesn't seem necessary. It is a small thing but I still feel compelled to mention it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

this::dropInBackground
);
final ListenableFuture<SegmentsAndMetadata> future = ListenableFutures
.transformAsync(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this line break doesn't seem necessary. It is a small thing but I still feel compelled to mention it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixing

/**
* This class is specialized for streaming ingestion. In streaming ingestion, the segment lifecycle is like:
*
* <p>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixing

synchronized (segments) {
sequenceNames.forEach(segments::remove);
}
final SettableFuture<SegmentsAndMetadata> future = SettableFuture.create();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change this to an async transform that generates a useless future, instead of keeping it as a normal transform?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not following, what do you mean by a "normal transform" here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I fixed this, but please check

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By "normal transform" I meant one that uses a regular Function rather than AsyncFunction. The new diff looks good to me now.

Comment thread codestyle/druid-forbidden-apis.txt Outdated
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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still different from other signatures. I'm not sure which syntax is correct, so please check that those prohibitions work

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@drcrallen
Copy link
Copy Markdown
Contributor Author

@gianm / @leventov any other comments?

@drcrallen
Copy link
Copy Markdown
Contributor Author

Thanks!

@gianm gianm merged commit 58f110f into apache:master Mar 20, 2018
@drcrallen drcrallen deleted the guava/lessGuavaCompatProblems branch March 20, 2018 16:12
@drcrallen drcrallen added this to the 0.13.0 milestone Apr 6, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants