-
Notifications
You must be signed in to change notification settings - Fork 8
fix: call option deadline timeout unchanged in query method call #310
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
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #310 +/- ##
==========================================
- Coverage 87.48% 87.22% -0.26%
==========================================
Files 20 20
Lines 1262 1284 +22
Branches 204 213 +9
==========================================
+ Hits 1104 1120 +16
- Misses 74 75 +1
- Partials 84 89 +5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| GrpcCallOptions grpcCallOptionsCopy = options.grpcCallOptions(); | ||
| if (grpcCallOptionsCopy.getDeadline() == null && config.getQueryTimeout() != null) { | ||
| grpcCallOptionsCopy = new GrpcCallOptions.Builder() | ||
| .fromGrpcCallOptions(grpcCallOptionsCopy) | ||
| .withDeadline(Deadline.after(config.getQueryTimeout().toMillis(), TimeUnit.MILLISECONDS)) | ||
| .build(); | ||
| } | ||
|
|
||
| CallOption[] callOptions = grpcCallOptionsCopy.getCallOptions(); |
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.
What about simplest solution?
| GrpcCallOptions grpcCallOptionsCopy = options.grpcCallOptions(); | |
| if (grpcCallOptionsCopy.getDeadline() == null && config.getQueryTimeout() != null) { | |
| grpcCallOptionsCopy = new GrpcCallOptions.Builder() | |
| .fromGrpcCallOptions(grpcCallOptionsCopy) | |
| .withDeadline(Deadline.after(config.getQueryTimeout().toMillis(), TimeUnit.MILLISECONDS)) | |
| .build(); | |
| } | |
| CallOption[] callOptions = grpcCallOptionsCopy.getCallOptions(); | |
| GrpcCallOptions.Builder builder = new GrpcCallOptions.Builder() | |
| .fromGrpcCallOptions(options.grpcCallOptions()); | |
| if (config.getQueryTimeout() != null) { | |
| builder.withDeadline(Deadline.after(config.getQueryTimeout().toMillis(), TimeUnit.MILLISECONDS)) | |
| } | |
| CallOption[] callOptions = builder.build().getCallOptions(); |
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.
I think we still need to check in line 421 whether grpcCallOptions in the passed options object, doesn't already have a Deadline defined. options.grpcCallOptions().getDeadline() passed in a single call should supersede config.getQueryTimeout() . But, I'll try something similar now locally.
But, yes I think we can reduce the complexity by getting rid of grpcCallOptionsCopy which was suggested by copilot.
| protected QueryOptions clone() { | ||
| QueryOptions clone; | ||
| HashMap<String, String> cloneHeaders = new HashMap<>(this.headers); | ||
| for (String key : this.headers.keySet()) { | ||
| cloneHeaders.put(key, this.headers.get(key)); | ||
| } | ||
| try { | ||
| clone = (QueryOptions) super.clone(); | ||
| } catch (final CloneNotSupportedException e) { | ||
| clone = new QueryOptions(this.database, this.queryType, cloneHeaders); | ||
| } | ||
| if (this.grpcCallOptions != null) { | ||
| GrpcCallOptions.Builder grpcOptsBuilder = new GrpcCallOptions.Builder(); | ||
| if (this.grpcCallOptions.getDeadline() != null) { | ||
| grpcOptsBuilder.withDeadline(this.grpcCallOptions.getDeadline().offset(0, TimeUnit.MILLISECONDS)); | ||
| } | ||
| grpcOptsBuilder.withExecutor(this.grpcCallOptions.getExecutor()); | ||
| grpcOptsBuilder.withCompressorName(this.grpcCallOptions.getCompressorName()); | ||
| if (this.grpcCallOptions.getWaitForReady() != null | ||
| && this.grpcCallOptions.getWaitForReady()) { | ||
| grpcOptsBuilder.withWaitForReady(); | ||
| } | ||
| grpcOptsBuilder.withMaxInboundMessageSize(this.grpcCallOptions.getMaxInboundMessageSize()); | ||
| grpcOptsBuilder.withMaxOutboundMessageSize(this.grpcCallOptions.getMaxOutboundMessageSize()); | ||
| clone.grpcCallOptions = grpcOptsBuilder.build(); | ||
| } | ||
| return clone; |
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.
Why this?
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 is mainly for the test queryOptionsUnchangedByCall(). A deep copy of QueryOptions from before when the query() call is made is needed for comparison to the QueryOptions object passed to query() after the call is made, to make sure that the query call did not mutate the passed QueryOptions object in any way.
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.
If this is only for testing, we should remove it. It will be difficult to maintain, because whenever a new property is added to GrpcCallOptions, we would have to update this as well.
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.
OK. I'll move it to a static method in the test class.
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.
thx
| if (options.grpcCallOptions().getDeadline() == null) { | ||
| if (config.getQueryTimeout() != null) { | ||
| builder.withDeadline(Deadline.after(config.getQueryTimeout().toMillis(), TimeUnit.MILLISECONDS)); | ||
| } | ||
| } else if (options.grpcCallOptions().getDeadline().timeRemaining(TimeUnit.NANOSECONDS) <= 0) { | ||
| LOG.warning("Received impractical gRPC call options deadline " | ||
| + options.grpcCallOptions().getDeadline()); | ||
| if (config.getQueryTimeout() != null) { | ||
| LOG.warning("Using configuration query timeout " | ||
| + config.getQueryTimeout() + " as a replacement."); | ||
| builder.withDeadline(Deadline.after(config.getQueryTimeout().toMillis(), TimeUnit.MILLISECONDS)); | ||
| } | ||
| } |
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.
What about something simple like:
| if (options.grpcCallOptions().getDeadline() == null) { | |
| if (config.getQueryTimeout() != null) { | |
| builder.withDeadline(Deadline.after(config.getQueryTimeout().toMillis(), TimeUnit.MILLISECONDS)); | |
| } | |
| } else if (options.grpcCallOptions().getDeadline().timeRemaining(TimeUnit.NANOSECONDS) <= 0) { | |
| LOG.warning("Received impractical gRPC call options deadline " | |
| + options.grpcCallOptions().getDeadline()); | |
| if (config.getQueryTimeout() != null) { | |
| LOG.warning("Using configuration query timeout " | |
| + config.getQueryTimeout() + " as a replacement."); | |
| builder.withDeadline(Deadline.after(config.getQueryTimeout().toMillis(), TimeUnit.MILLISECONDS)); | |
| } | |
| } | |
| if (config.getQueryTimeout() != null) { | |
| builder.withDeadline(Deadline.after(config.getQueryTimeout().toMillis(), TimeUnit.MILLISECONDS)); | |
| } else if (options.grpcCallOptions().getDeadline() != null) { | |
| LOG.warning("No query timeout configured in ClientConfig, but gRPC call options contains a deadline " | |
| + options.grpcCallOptions().getDeadline() | |
| + ". This may lead to unexpected behavior - use setting query timeout in ClientConfig."); | |
| } |
?
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.
Updated logic based on further conversation.
… QueryOptions.clone()
| // Assertions.assertThat(thrown).isInstanceOf(FlightRuntimeException.class); | ||
| // Assertions.assertThat(thrown.getMessage()).matches(".*deadline.*exceeded.*"); |
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.
Please remove or uncomment this.
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.
removed.
bednar
left a comment
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.
Update JavaDoc in GrpcCallIOptions.Builder.withDeadline that the preferred way is set deadline globally.
bednar
left a comment
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.
LGTM 🚀
Closes #301
Proposed Changes
queryDatamethod, leave theQueryOptionsargument unchanged. Use instead a local copy.Explanation: The passed
QueryOptionsobject might be reused, in which case mutating a field likeDeadlineinGrpcOptionscould lead to reusing an already expired deadline in future calls.QueryOptionsobject has not been modified. N.B. in support of this test theclonemethod is overridden to make a deep copy of the original.N.B. IDE reformatted indentations in the QueryOptionsTest.java file.
Checklist