-
Notifications
You must be signed in to change notification settings - Fork 4k
core: add perAttemptRecvTimeout to retry policy #8301
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
Changes from all commits
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 |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ | |
| import static io.grpc.MethodDescriptor.MethodType.UNARY; | ||
| import static io.grpc.Status.Code.UNAVAILABLE; | ||
| import static java.util.concurrent.TimeUnit.MILLISECONDS; | ||
| import static org.junit.Assert.fail; | ||
|
|
||
| import com.google.common.collect.ImmutableList; | ||
| import com.google.common.collect.ImmutableMap; | ||
|
|
@@ -155,13 +156,14 @@ public void managedChannelServiceConfig_parseMethodConfig() { | |
| "name", ImmutableList.of(name1, name2), | ||
| "timeout", "1.234s", | ||
| "retryPolicy", | ||
| ImmutableMap.of( | ||
| "maxAttempts", 3.0D, | ||
| "initialBackoff", "1s", | ||
| "maxBackoff", "10s", | ||
| "backoffMultiplier", 1.5D, | ||
| "retryableStatusCodes", ImmutableList.of("UNAVAILABLE") | ||
| )); | ||
| ImmutableMap.builder() | ||
| .put("maxAttempts", 3.0D) | ||
| .put("initialBackoff", "1s") | ||
| .put("maxBackoff", "10s") | ||
| .put("backoffMultiplier", 1.5D) | ||
| .put("perAttemptRecvTimeout", "2.5s") | ||
| .put("retryableStatusCodes", ImmutableList.of("UNAVAILABLE")) | ||
| .build()); | ||
| Map<String, ?> defaultMethodConfig = ImmutableMap.of( | ||
| "name", ImmutableList.of(ImmutableMap.of()), | ||
| "timeout", "4.321s"); | ||
|
|
@@ -187,6 +189,8 @@ public void managedChannelServiceConfig_parseMethodConfig() { | |
| methodInfo = serviceConfig.getMethodConfig(methodForName("service1", "method1")); | ||
| assertThat(methodInfo.timeoutNanos).isEqualTo(MILLISECONDS.toNanos(1234)); | ||
| assertThat(methodInfo.retryPolicy.maxAttempts).isEqualTo(2); | ||
| assertThat(methodInfo.retryPolicy.perAttemptRecvTimeoutNanos) | ||
| .isEqualTo(MILLISECONDS.toNanos(2500)); | ||
| assertThat(methodInfo.retryPolicy.retryableStatusCodes).containsExactly(UNAVAILABLE); | ||
| methodInfo = serviceConfig.getMethodConfig(methodForName("service1", "methodX")); | ||
| assertThat(methodInfo.timeoutNanos).isEqualTo(MILLISECONDS.toNanos(4321)); | ||
|
|
@@ -212,6 +216,84 @@ public void getDefaultConfigSelectorFromConfig() { | |
| .isEqualTo(serviceConfig.getMethodConfig(method)); | ||
| } | ||
|
|
||
| @Test | ||
| public void retryConfig_emptyRetriableStatusCodesAllowedWithPerAttemptRecvTimeoutGiven() { | ||
| Map<String, ?> retryPolicy = ImmutableMap.<String, Object>builder() | ||
| .put("maxAttempts", 3.0D) | ||
| .put("initialBackoff", "1s") | ||
| .put("maxBackoff", "10s") | ||
| .put("backoffMultiplier", 1.5D) | ||
| .put("perAttemptRecvTimeout", "2.5s") | ||
| .put("retryableStatusCodes", ImmutableList.of()) | ||
| .build(); | ||
| Map<String, ?> methodConfig = ImmutableMap.of( | ||
| "name", ImmutableList.of(ImmutableMap.of()), "retryPolicy", retryPolicy); | ||
| Map<String, ?> rawServiceConfig = | ||
| ImmutableMap.of("methodConfig", ImmutableList.of(methodConfig)); | ||
| assertThat(ManagedChannelServiceConfig.fromServiceConfig(rawServiceConfig, true, 5, 5, null)) | ||
| .isNotNull(); | ||
| } | ||
|
|
||
| @Test | ||
| public void retryConfig_PerAttemptRecvTimeoutUnsetAllowedIfRetryableStatusCodesNonempty() { | ||
| Map<String, ?> retryPolicy = ImmutableMap.<String, Object>builder() | ||
| .put("maxAttempts", 3.0D) | ||
| .put("initialBackoff", "1s") | ||
| .put("maxBackoff", "10s") | ||
| .put("backoffMultiplier", 1.5D) | ||
| .put("retryableStatusCodes", ImmutableList.of("UNAVAILABLE")) | ||
| .build(); | ||
| Map<String, ?> methodConfig = ImmutableMap.of( | ||
| "name", ImmutableList.of(ImmutableMap.of()), "retryPolicy", retryPolicy); | ||
| Map<String, ?> rawServiceConfig = | ||
| ImmutableMap.of("methodConfig", ImmutableList.of(methodConfig)); | ||
| assertThat(ManagedChannelServiceConfig.fromServiceConfig(rawServiceConfig, true, 5, 5, null)) | ||
| .isNotNull(); | ||
| } | ||
|
|
||
| @Test | ||
| public void retryConfig_emptyRetriableStatusCodesNotAllowedWithPerAttemptRecvTimeoutUnset() { | ||
| Map<String, ?> retryPolicy = ImmutableMap.<String, Object>builder() | ||
| .put("maxAttempts", 3.0D) | ||
| .put("initialBackoff", "1s") | ||
| .put("maxBackoff", "10s") | ||
| .put("backoffMultiplier", 1.5D) | ||
| .put("retryableStatusCodes", ImmutableList.of()) | ||
| .build(); | ||
| Map<String, ?> methodConfig = ImmutableMap.of( | ||
| "name", ImmutableList.of(ImmutableMap.of()), "retryPolicy", retryPolicy); | ||
| Map<String, ?> rawServiceConfig = | ||
| ImmutableMap.of("methodConfig", ImmutableList.of(methodConfig)); | ||
| try { | ||
| ManagedChannelServiceConfig.fromServiceConfig(rawServiceConfig, true, 5, 5, null); | ||
| fail("The expected IllegalArgumentException is not thrown"); | ||
| } catch (IllegalArgumentException e) { | ||
| assertThat(e).hasMessageThat().contains( | ||
| "retryableStatusCodes cannot be empty without perAttemptRecvTimeout"); | ||
| } | ||
| } | ||
|
|
||
| // For now we allow perAttemptRecvTimeout being zero although it does not make sense. | ||
| // TODO(zdapeng): disallow zero perAttemptRecvTimeout if hedging is not enabled once we support | ||
|
Member
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. Once retries are no longer experimental, we can't be adding a restriction. Right? And it sounded like your plan was to try to get retries stable here before supporting hedging.
Contributor
Author
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.
That's right.
Why? I'm not. I'm currently allowing zero perAttemptRecvTimeout, because in the future, we have usecase for hedging.
Member
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. Oh, I see. |
||
| // hedge_on_per_try_timeout. | ||
| @Test | ||
| public void retryConfig_AllowPerAttemptRecvTimeoutZero() { | ||
| Map<String, ?> retryPolicy = ImmutableMap.<String, Object>builder() | ||
| .put("maxAttempts", 3.0D) | ||
| .put("initialBackoff", "1s") | ||
| .put("maxBackoff", "10s") | ||
| .put("backoffMultiplier", 1.5D) | ||
| .put("perAttemptRecvTimeout", "0s") | ||
| .put("retryableStatusCodes", ImmutableList.of()) | ||
| .build(); | ||
| Map<String, ?> methodConfig = ImmutableMap.of( | ||
| "name", ImmutableList.of(ImmutableMap.of()), "retryPolicy", retryPolicy); | ||
| Map<String, ?> rawServiceConfig = | ||
| ImmutableMap.of("methodConfig", ImmutableList.of(methodConfig)); | ||
| assertThat(ManagedChannelServiceConfig.fromServiceConfig(rawServiceConfig, true, 5, 5, null)) | ||
| .isNotNull(); | ||
| } | ||
|
|
||
| private static MethodDescriptor<?, ?> methodForName(String service, String method) { | ||
| return MethodDescriptor.<Void, Void>newBuilder() | ||
| .setFullMethodName(service + "/" + method) | ||
|
|
||
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.
No new tests? Seems we need tests for these cases.
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 do we need tests for assertions. They will throw if the app is in wrong state and people will notice.
Uh oh!
There was an error while loading. Please reload this page.
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.
Say if I had a bug here and the code is like
with
&¬||. Then existing code going through this code path would throw.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.
The unit tests don't look like they would fail. If we can pass one or the other, it seems there should be tests for passing one or the other. I wouldn't be such a stickler but this is for service config and a bug in the logic can poison the 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.
Done.