core, examples: allow empty nonfatalStatusCodes, and integer status codes

This commit is contained in:
ZHANG Dapeng 2019-06-25 15:29:52 -07:00 committed by GitHub
parent 84f8bac8d4
commit 1e04dcf5c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 55 additions and 47 deletions

View File

@ -18,21 +18,17 @@ package io.grpc.internal;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Verify.verify;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import io.grpc.MethodDescriptor; import io.grpc.MethodDescriptor;
import io.grpc.Status.Code;
import io.grpc.internal.RetriableStream.Throttle; import io.grpc.internal.RetriableStream.Throttle;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
@ -256,21 +252,9 @@ final class ManagedChannelServiceConfig {
"backoffMultiplier must be greater than 0: %s", "backoffMultiplier must be greater than 0: %s",
backoffMultiplier); backoffMultiplier);
List<String> rawCodes =
ServiceConfigUtil.getRetryableStatusCodesFromRetryPolicy(retryPolicy);
checkNotNull(rawCodes, "rawCodes must be present");
checkArgument(!rawCodes.isEmpty(), "rawCodes can't be empty");
EnumSet<Code> codes = EnumSet.noneOf(Code.class);
// service config doesn't say if duplicates are allowed, so just accept them.
for (String rawCode : rawCodes) {
verify(!"OK".equals(rawCode), "rawCode can not be \"OK\"");
codes.add(Code.valueOf(rawCode));
}
Set<Code> retryableStatusCodes = Collections.unmodifiableSet(codes);
return new RetryPolicy( return new RetryPolicy(
maxAttempts, initialBackoffNanos, maxBackoffNanos, backoffMultiplier, maxAttempts, initialBackoffNanos, maxBackoffNanos, backoffMultiplier,
retryableStatusCodes); ServiceConfigUtil.getRetryableStatusCodesFromRetryPolicy(retryPolicy));
} }
private static HedgingPolicy hedgingPolicy( private static HedgingPolicy hedgingPolicy(
@ -287,19 +271,9 @@ final class ManagedChannelServiceConfig {
checkArgument( checkArgument(
hedgingDelayNanos >= 0, "hedgingDelay must not be negative: %s", hedgingDelayNanos); hedgingDelayNanos >= 0, "hedgingDelay must not be negative: %s", hedgingDelayNanos);
List<String> rawCodes = return new HedgingPolicy(
ServiceConfigUtil.getNonFatalStatusCodesFromHedgingPolicy(hedgingPolicy); maxAttempts, hedgingDelayNanos,
checkNotNull(rawCodes, "rawCodes must be present"); ServiceConfigUtil.getNonFatalStatusCodesFromHedgingPolicy(hedgingPolicy));
checkArgument(!rawCodes.isEmpty(), "rawCodes can't be empty");
EnumSet<Code> codes = EnumSet.noneOf(Code.class);
// service config doesn't say if duplicates are allowed, so just accept them.
for (String rawCode : rawCodes) {
verify(!"OK".equals(rawCode), "rawCode can not be \"OK\"");
codes.add(Code.valueOf(rawCode));
}
Set<Code> nonFatalStatusCodes = Collections.unmodifiableSet(codes);
return new HedgingPolicy(maxAttempts, hedgingDelayNanos, nonFatalStatusCodes);
} }
} }
} }

View File

@ -18,18 +18,23 @@ package io.grpc.internal;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verify;
import static com.google.common.math.LongMath.checkedAdd; import static com.google.common.math.LongMath.checkedAdd;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.VerifyException;
import io.grpc.Status;
import io.grpc.internal.RetriableStream.Throttle; import io.grpc.internal.RetriableStream.Throttle;
import java.text.ParseException; import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -176,12 +181,43 @@ public final class ServiceConfigUtil {
return getDouble(retryPolicy, RETRY_POLICY_BACKOFF_MULTIPLIER_KEY); return getDouble(retryPolicy, RETRY_POLICY_BACKOFF_MULTIPLIER_KEY);
} }
@Nullable private static Set<Status.Code> getStatusCodesFromList(List<?> statuses) {
static List<String> getRetryableStatusCodesFromRetryPolicy(Map<String, ?> retryPolicy) { EnumSet<Status.Code> codes = EnumSet.noneOf(Status.Code.class);
if (!retryPolicy.containsKey(RETRY_POLICY_RETRYABLE_STATUS_CODES_KEY)) { for (Object status : statuses) {
return null; Status.Code code;
if (status instanceof Double) {
Double statusD = (Double) status;
int codeValue = statusD.intValue();
verify((double) codeValue == statusD, "Status code %s is not integral", status);
code = Status.fromCodeValue(codeValue).getCode();
verify(code.value() == statusD.intValue(), "Status code %s is not valid", status);
} else if (status instanceof String) {
try {
code = Status.Code.valueOf((String) status);
} catch (IllegalArgumentException iae) {
throw new VerifyException("Status code " + status + " is not valid", iae);
} }
return checkStringList(getList(retryPolicy, RETRY_POLICY_RETRYABLE_STATUS_CODES_KEY)); } else {
throw new VerifyException(
"Can not convert status code " + status + " to Status.Code, because it's type is "
+ status.getClass());
}
codes.add(code);
}
return Collections.unmodifiableSet(codes);
}
static Set<Status.Code> getRetryableStatusCodesFromRetryPolicy(Map<String, ?> retryPolicy) {
verify(
retryPolicy.containsKey(RETRY_POLICY_RETRYABLE_STATUS_CODES_KEY),
"%s is required in retry policy", RETRY_POLICY_RETRYABLE_STATUS_CODES_KEY);
Set<Status.Code> codes =
getStatusCodesFromList(getList(retryPolicy, RETRY_POLICY_RETRYABLE_STATUS_CODES_KEY));
verify(!codes.isEmpty(), "%s must not be empty", RETRY_POLICY_RETRYABLE_STATUS_CODES_KEY);
verify(
!codes.contains(Status.Code.OK),
"%s must not contain OK", RETRY_POLICY_RETRYABLE_STATUS_CODES_KEY);
return codes;
} }
@Nullable @Nullable
@ -205,12 +241,16 @@ public final class ServiceConfigUtil {
} }
} }
@Nullable static Set<Status.Code> getNonFatalStatusCodesFromHedgingPolicy(Map<String, ?> hedgingPolicy) {
static List<String> getNonFatalStatusCodesFromHedgingPolicy(Map<String, ?> hedgingPolicy) {
if (!hedgingPolicy.containsKey(HEDGING_POLICY_NON_FATAL_STATUS_CODES_KEY)) { if (!hedgingPolicy.containsKey(HEDGING_POLICY_NON_FATAL_STATUS_CODES_KEY)) {
return null; return Collections.unmodifiableSet(EnumSet.noneOf(Status.Code.class));
} }
return checkStringList(getList(hedgingPolicy, HEDGING_POLICY_NON_FATAL_STATUS_CODES_KEY)); Set<Status.Code> codes =
getStatusCodesFromList(getList(hedgingPolicy, HEDGING_POLICY_NON_FATAL_STATUS_CODES_KEY));
verify(
!codes.contains(Status.Code.OK),
"%s must not contain OK", HEDGING_POLICY_NON_FATAL_STATUS_CODES_KEY);
return codes;
} }
@Nullable @Nullable

View File

@ -47,10 +47,7 @@ before trying out the examples.
```json ```json
"hedgingPolicy": { "hedgingPolicy": {
"maxAttempts": 3, "maxAttempts": 3,
"hedgingDelay": "1s", "hedgingDelay": "1s"
"nonFatalStatusCodes": [
"UNAVAILABLE"
]
} }
``` ```
Then the latency summary in the client log is like the following Then the latency summary in the client log is like the following

View File

@ -10,10 +10,7 @@
"hedgingPolicy": { "hedgingPolicy": {
"maxAttempts": 3, "maxAttempts": 3,
"hedgingDelay": "1s", "hedgingDelay": "1s"
"nonFatalStatusCodes": [
"UNAVAILABLE"
]
} }
} }
], ],