-
Notifications
You must be signed in to change notification settings - Fork 119
feat: support retry settings and retryable codes in call context #1238
Changes from 17 commits
8eeefdc
83c2f16
aabd3b0
d73e7b3
c58f9c4
ed22eaf
80230e1
8ef6e6f
19ed8d0
b2999d6
3eb9fa7
73b26c7
111f8f5
a58eb10
51b670b
41fdc4e
68b2c69
f6beeef
54f9f70
da9cc81
8c5090d
131da46
395d7cf
5c90e70
301069d
f322549
e0a61c5
0925ea2
e02f951
a0118c3
eafe239
ffa05a1
8ae3280
3c0d719
5da364a
a42ea60
8eab974
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 |
---|---|---|
|
@@ -29,16 +29,19 @@ | |
*/ | ||
package com.google.api.gax.grpc; | ||
|
||
import com.google.api.core.BetaApi; | ||
import com.google.api.core.InternalExtensionOnly; | ||
import com.google.api.gax.retrying.RetrySettings; | ||
import com.google.api.gax.rpc.ApiCallContext; | ||
import com.google.api.gax.rpc.StatusCode; | ||
import com.google.api.gax.rpc.TransportChannel; | ||
import com.google.api.gax.rpc.internal.Headers; | ||
import com.google.api.gax.tracing.ApiTracer; | ||
import com.google.api.gax.tracing.NoopApiTracer; | ||
import com.google.auth.Credentials; | ||
import com.google.common.annotations.Beta; | ||
import com.google.common.base.Preconditions; | ||
import com.google.common.collect.ImmutableMap; | ||
import com.google.common.collect.ImmutableSet; | ||
import io.grpc.CallCredentials; | ||
import io.grpc.CallOptions; | ||
import io.grpc.CallOptions.Key; | ||
|
@@ -49,6 +52,7 @@ | |
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
import javax.annotation.Nonnull; | ||
import javax.annotation.Nullable; | ||
import org.threeten.bp.Duration; | ||
|
@@ -60,8 +64,11 @@ | |
* GrpcCallContext itself or the underlying data. Methods of the form {@code withX}, such as {@link | ||
* #withTransportChannel}, return copies of the object, but with one field changed. The immutability | ||
* and thread safety of the arguments solely depends on the arguments themselves. | ||
* | ||
* <p>Applications should reference {@link ApiCallContext} instead. This class is likely to | ||
* experience breaking changes. | ||
*/ | ||
@BetaApi("Reference ApiCallContext instead - this class is likely to experience breaking changes") | ||
@Beta | ||
@InternalExtensionOnly | ||
public final class GrpcCallContext implements ApiCallContext { | ||
static final CallOptions.Key<ApiTracer> TRACER_KEY = Key.create("gax.tracer"); | ||
|
@@ -72,18 +79,36 @@ public final class GrpcCallContext implements ApiCallContext { | |
@Nullable private final Duration streamWaitTimeout; | ||
@Nullable private final Duration streamIdleTimeout; | ||
@Nullable private final Integer channelAffinity; | ||
@Nullable private final RetrySettings retrySettings; | ||
@Nullable private final ImmutableSet<StatusCode.Code> retryableCodes; | ||
private final ImmutableMap<String, List<String>> extraHeaders; | ||
|
||
/** Returns an empty instance with a null channel and default {@link CallOptions}. */ | ||
public static GrpcCallContext createDefault() { | ||
return new GrpcCallContext( | ||
null, CallOptions.DEFAULT, null, null, null, null, ImmutableMap.<String, List<String>>of()); | ||
null, | ||
CallOptions.DEFAULT, | ||
null, | ||
null, | ||
null, | ||
null, | ||
ImmutableMap.<String, List<String>>of(), | ||
null, | ||
null); | ||
} | ||
|
||
/** Returns an instance with the given channel and {@link CallOptions}. */ | ||
public static GrpcCallContext of(Channel channel, CallOptions callOptions) { | ||
return new GrpcCallContext( | ||
channel, callOptions, null, null, null, null, ImmutableMap.<String, List<String>>of()); | ||
channel, | ||
callOptions, | ||
null, | ||
null, | ||
null, | ||
null, | ||
ImmutableMap.<String, List<String>>of(), | ||
null, | ||
null); | ||
} | ||
|
||
private GrpcCallContext( | ||
|
@@ -93,14 +118,18 @@ private GrpcCallContext( | |
@Nullable Duration streamWaitTimeout, | ||
@Nullable Duration streamIdleTimeout, | ||
@Nullable Integer channelAffinity, | ||
ImmutableMap<String, List<String>> extraHeaders) { | ||
ImmutableMap<String, List<String>> extraHeaders, | ||
@Nullable RetrySettings retrySettings, | ||
@Nullable Set<StatusCode.Code> retryableCodes) { | ||
this.channel = channel; | ||
this.callOptions = Preconditions.checkNotNull(callOptions); | ||
this.timeout = timeout; | ||
this.streamWaitTimeout = streamWaitTimeout; | ||
this.streamIdleTimeout = streamIdleTimeout; | ||
this.channelAffinity = channelAffinity; | ||
this.extraHeaders = Preconditions.checkNotNull(extraHeaders); | ||
this.retrySettings = retrySettings; | ||
this.retryableCodes = retryableCodes == null ? null : ImmutableSet.copyOf(retryableCodes); | ||
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 is this nullable? per Effective Java, empty collections are preferred. 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. There is a difference in this case:
I've added that to the documentation of |
||
} | ||
|
||
/** | ||
|
@@ -162,7 +191,9 @@ public GrpcCallContext withTimeout(@Nullable Duration timeout) { | |
this.streamWaitTimeout, | ||
this.streamIdleTimeout, | ||
this.channelAffinity, | ||
this.extraHeaders); | ||
this.extraHeaders, | ||
retrySettings, | ||
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. should it be 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. Yes, done (and cleaned up the other instances). |
||
this.retryableCodes); | ||
} | ||
|
||
@Nullable | ||
|
@@ -185,7 +216,9 @@ public GrpcCallContext withStreamWaitTimeout(@Nullable Duration streamWaitTimeou | |
streamWaitTimeout, | ||
streamIdleTimeout, | ||
channelAffinity, | ||
extraHeaders); | ||
extraHeaders, | ||
retrySettings, | ||
retryableCodes); | ||
} | ||
|
||
@Override | ||
|
@@ -202,10 +235,12 @@ public GrpcCallContext withStreamIdleTimeout(@Nullable Duration streamIdleTimeou | |
streamWaitTimeout, | ||
streamIdleTimeout, | ||
channelAffinity, | ||
extraHeaders); | ||
extraHeaders, | ||
retrySettings, | ||
retryableCodes); | ||
} | ||
|
||
@BetaApi("The surface for channel affinity is not stable yet and may change in the future.") | ||
@Beta | ||
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. Please revert back to |
||
public GrpcCallContext withChannelAffinity(@Nullable Integer affinity) { | ||
return new GrpcCallContext( | ||
channel, | ||
|
@@ -214,10 +249,12 @@ public GrpcCallContext withChannelAffinity(@Nullable Integer affinity) { | |
streamWaitTimeout, | ||
streamIdleTimeout, | ||
affinity, | ||
extraHeaders); | ||
extraHeaders, | ||
retrySettings, | ||
retryableCodes); | ||
} | ||
|
||
@BetaApi("The surface for extra headers is not stable yet and may change in the future.") | ||
@Beta | ||
@Override | ||
public GrpcCallContext withExtraHeaders(Map<String, List<String>> extraHeaders) { | ||
Preconditions.checkNotNull(extraHeaders); | ||
|
@@ -230,7 +267,47 @@ public GrpcCallContext withExtraHeaders(Map<String, List<String>> extraHeaders) | |
streamWaitTimeout, | ||
streamIdleTimeout, | ||
channelAffinity, | ||
newExtraHeaders); | ||
newExtraHeaders, | ||
retrySettings, | ||
retryableCodes); | ||
} | ||
|
||
@Override | ||
public RetrySettings getRetrySettings() { | ||
return this.retrySettings; | ||
} | ||
|
||
@Override | ||
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. What is the difference between the retry settings here, and those in UnaryCallSettings? 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.
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. Furthermore, |
||
public GrpcCallContext withRetrySettings(RetrySettings retrySettings) { | ||
return new GrpcCallContext( | ||
this.channel, | ||
this.callOptions, | ||
this.timeout, | ||
this.streamWaitTimeout, | ||
this.streamIdleTimeout, | ||
this.channelAffinity, | ||
this.extraHeaders, | ||
retrySettings, | ||
retryableCodes); | ||
} | ||
|
||
@Override | ||
public Set<StatusCode.Code> getRetryableCodes() { | ||
return this.retryableCodes; | ||
} | ||
|
||
@Override | ||
public GrpcCallContext withRetryableCodes(Set<StatusCode.Code> retryableCodes) { | ||
return new GrpcCallContext( | ||
this.channel, | ||
this.callOptions, | ||
this.timeout, | ||
this.streamWaitTimeout, | ||
this.streamIdleTimeout, | ||
this.channelAffinity, | ||
this.extraHeaders, | ||
this.retrySettings, | ||
retryableCodes); | ||
} | ||
|
||
@Override | ||
|
@@ -285,6 +362,16 @@ public ApiCallContext merge(ApiCallContext inputCallContext) { | |
newChannelAffinity = this.channelAffinity; | ||
} | ||
|
||
RetrySettings newRetrySettings = grpcCallContext.retrySettings; | ||
if (newRetrySettings == null) { | ||
newRetrySettings = this.retrySettings; | ||
} | ||
|
||
Set<StatusCode.Code> newRetryableCodes = grpcCallContext.retryableCodes; | ||
if (newRetryableCodes == null) { | ||
newRetryableCodes = this.retryableCodes; | ||
} | ||
|
||
ImmutableMap<String, List<String>> newExtraHeaders = | ||
Headers.mergeHeaders(extraHeaders, grpcCallContext.extraHeaders); | ||
|
||
|
@@ -305,7 +392,9 @@ public ApiCallContext merge(ApiCallContext inputCallContext) { | |
newStreamWaitTimeout, | ||
newStreamIdleTimeout, | ||
newChannelAffinity, | ||
newExtraHeaders); | ||
newExtraHeaders, | ||
newRetrySettings, | ||
newRetryableCodes); | ||
} | ||
|
||
/** The {@link Channel} set on this context. */ | ||
|
@@ -323,7 +412,7 @@ public CallOptions getCallOptions() { | |
* | ||
* @see ApiCallContext#withStreamWaitTimeout(Duration) | ||
*/ | ||
@BetaApi("The surface for streaming is not stable yet and may change in the future.") | ||
@Beta | ||
@Nullable | ||
public Duration getStreamWaitTimeout() { | ||
return streamWaitTimeout; | ||
|
@@ -334,21 +423,21 @@ public Duration getStreamWaitTimeout() { | |
* | ||
* @see ApiCallContext#withStreamIdleTimeout(Duration) | ||
*/ | ||
@BetaApi("The surface for streaming is not stable yet and may change in the future.") | ||
@Beta | ||
@Nullable | ||
public Duration getStreamIdleTimeout() { | ||
return streamIdleTimeout; | ||
} | ||
|
||
/** The channel affinity for this context. */ | ||
@BetaApi("The surface for channel affinity is not stable yet and may change in the future.") | ||
@Beta | ||
@Nullable | ||
public Integer getChannelAffinity() { | ||
return channelAffinity; | ||
} | ||
|
||
/** The extra header for this context. */ | ||
@BetaApi("The surface for extra headers is not stable yet and may change in the future.") | ||
@Beta | ||
@Override | ||
public Map<String, List<String>> getExtraHeaders() { | ||
return this.extraHeaders; | ||
|
@@ -363,7 +452,9 @@ public GrpcCallContext withChannel(Channel newChannel) { | |
this.streamWaitTimeout, | ||
this.streamIdleTimeout, | ||
this.channelAffinity, | ||
this.extraHeaders); | ||
this.extraHeaders, | ||
this.retrySettings, | ||
this.retryableCodes); | ||
} | ||
|
||
/** Returns a new instance with the call options set to the given call options. */ | ||
|
@@ -375,7 +466,9 @@ public GrpcCallContext withCallOptions(CallOptions newCallOptions) { | |
this.streamWaitTimeout, | ||
this.streamIdleTimeout, | ||
this.channelAffinity, | ||
this.extraHeaders); | ||
this.extraHeaders, | ||
this.retrySettings, | ||
this.retryableCodes); | ||
} | ||
|
||
public GrpcCallContext withRequestParamsDynamicHeaderOption(String requestParams) { | ||
|
@@ -412,7 +505,9 @@ public int hashCode() { | |
streamWaitTimeout, | ||
streamIdleTimeout, | ||
channelAffinity, | ||
extraHeaders); | ||
extraHeaders, | ||
retrySettings, | ||
retryableCodes); | ||
} | ||
|
||
@Override | ||
|
@@ -431,7 +526,9 @@ public boolean equals(Object o) { | |
&& Objects.equals(streamWaitTimeout, that.streamWaitTimeout) | ||
&& Objects.equals(streamIdleTimeout, that.streamIdleTimeout) | ||
&& Objects.equals(channelAffinity, that.channelAffinity) | ||
&& Objects.equals(extraHeaders, that.extraHeaders); | ||
&& Objects.equals(extraHeaders, that.extraHeaders) | ||
&& Objects.equals(retrySettings, that.retrySettings) | ||
&& Objects.equals(retryableCodes, that.retryableCodes); | ||
} | ||
|
||
Metadata getMetadata() { | ||
|
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 defined in guava, guava has been explicitly shaded from the gax surface.
@BetaApi
annotation must be used instead, as it is used in the rest of the gax.