Skip to content

Commit e270262

Browse files
ejona86AgraVator
authored andcommitted
Include causal status details in higher-level statuses
When an operation fails and we want to produce a new status at a higher level, we commonly are turning the first status into an exception to attach to the new exception. We should instead prefer to keep as much information in the status description itself, as cause is not as reliable to be logged/propagated. I do expect long-term we'll want to expose an API in grpc-api for this, but for the moment let's keep it internal. In particular, we'd have to figure out its name. I could also believe we might want different formatting, which becomes a clearer discussion when we can see the usages. I'm pretty certain there are some other places that could benefit from this utility, as I remember really wishing I had these functions a month or two ago. But these are the places I could immediately find. OutlierDetectionLoadBalancerConfig had its status code changed from INTERNAL to UNAVAILABLE because the value comes externally, and so isn't a gRPC bug or such. I didn't change the xds policies in the same way because it's murkier as the configuration for those is largely generated within xds itself.
1 parent 11755b4 commit e270262

6 files changed

Lines changed: 44 additions & 21 deletions

File tree

core/src/main/java/io/grpc/internal/GrpcUtil.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,31 @@ public static Status replaceInappropriateControlPlaneStatus(Status status) {
821821
+ status.getDescription()).withCause(status.getCause()) : status;
822822
}
823823

824+
/**
825+
* Returns a "clean" representation of a status code and description (not cause) like
826+
* "UNAVAILABLE: The description". Should be similar to Status.formatThrowableMessage().
827+
*/
828+
public static String statusToPrettyString(Status status) {
829+
if (status.getDescription() == null) {
830+
return status.getCode().toString();
831+
} else {
832+
return status.getCode() + ": " + status.getDescription();
833+
}
834+
}
835+
836+
/**
837+
* Create a status with contextual information, propagating details from a non-null status that
838+
* contributed to the failure. For example, if UNAVAILABLE, "Couldn't load bar", and status
839+
* "FAILED_PRECONDITION: Foo missing" were passed as arguments, then this method would produce the
840+
* status "UNAVAILABLE: Couldn't load bar: FAILED_PRECONDITION: Foo missing" with a cause if the
841+
* passed status had a cause.
842+
*/
843+
public static Status statusWithDetails(Status.Code code, String description, Status causeStatus) {
844+
return code.toStatus()
845+
.withDescription(description + ": " + statusToPrettyString(causeStatus))
846+
.withCause(causeStatus.getCause());
847+
}
848+
824849
/**
825850
* Checks whether the given item exists in the iterable. This is copied from Guava Collect's
826851
* {@code Iterables.contains()} because Guava Collect is not Android-friendly thus core can't

core/src/main/java/io/grpc/internal/RetriableStream.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -938,9 +938,8 @@ public void run() {
938938
&& localOnlyTransparentRetries.incrementAndGet() > 1_000) {
939939
commitAndRun(substream);
940940
if (state.winningSubstream == substream) {
941-
Status tooManyTransparentRetries = Status.INTERNAL
942-
.withDescription("Too many transparent retries. Might be a bug in gRPC")
943-
.withCause(status.asRuntimeException());
941+
Status tooManyTransparentRetries = GrpcUtil.statusWithDetails(
942+
Status.Code.INTERNAL, "Too many transparent retries. Might be a bug in gRPC", status);
944943
safeCloseMasterListener(tooManyTransparentRetries, rpcProgress, trailers);
945944
}
946945
return;

opentelemetry/src/main/java/io/grpc/opentelemetry/OpenTelemetryTracingModule.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import io.grpc.ServerCallHandler;
3838
import io.grpc.ServerInterceptor;
3939
import io.grpc.ServerStreamTracer;
40+
import io.grpc.internal.GrpcUtil;
4041
import io.grpc.opentelemetry.internal.OpenTelemetryConstants;
4142
import io.opentelemetry.api.OpenTelemetry;
4243
import io.opentelemetry.api.baggage.Baggage;
@@ -476,19 +477,11 @@ private void recordInboundMessageSize(Span span, int seqNo, long bytes) {
476477
span.addEvent("Inbound message", attributesBuilder.build());
477478
}
478479

479-
private String generateErrorStatusDescription(io.grpc.Status status) {
480-
if (status.getDescription() != null) {
481-
return status.getCode() + ": " + status.getDescription();
482-
} else {
483-
return status.getCode().toString();
484-
}
485-
}
486-
487480
private void endSpanWithStatus(Span span, io.grpc.Status status) {
488481
if (status.isOk()) {
489482
span.setStatus(StatusCode.OK);
490483
} else {
491-
span.setStatus(StatusCode.ERROR, generateErrorStatusDescription(status));
484+
span.setStatus(StatusCode.ERROR, GrpcUtil.statusToPrettyString(status));
492485
}
493486
span.end();
494487
}

util/src/main/java/io/grpc/util/OutlierDetectionLoadBalancerProvider.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.grpc.LoadBalancerProvider;
2424
import io.grpc.NameResolver.ConfigOrError;
2525
import io.grpc.Status;
26+
import io.grpc.internal.GrpcUtil;
2627
import io.grpc.internal.JsonUtil;
2728
import io.grpc.util.OutlierDetectionLoadBalancer.OutlierDetectionLoadBalancerConfig;
2829
import io.grpc.util.OutlierDetectionLoadBalancer.OutlierDetectionLoadBalancerConfig.FailurePercentageEjection;
@@ -148,9 +149,10 @@ private ConfigOrError parseLoadBalancingPolicyConfigInternal(Map<String, ?> rawC
148149
ConfigOrError childConfig = GracefulSwitchLoadBalancer.parseLoadBalancingPolicyConfig(
149150
JsonUtil.getListOfObjects(rawConfig, "childPolicy"));
150151
if (childConfig.getError() != null) {
151-
return ConfigOrError.fromError(Status.INTERNAL
152-
.withDescription("Failed to parse child in outlier_detection_experimental: " + rawConfig)
153-
.withCause(childConfig.getError().asRuntimeException()));
152+
return ConfigOrError.fromError(GrpcUtil.statusWithDetails(
153+
Status.Code.UNAVAILABLE,
154+
"Failed to parse child in outlier_detection_experimental",
155+
childConfig.getError()));
154156
}
155157
configBuilder.setChildConfig(childConfig.getConfig());
156158

xds/src/main/java/io/grpc/xds/WeightedTargetLoadBalancerProvider.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import io.grpc.LoadBalancerRegistry;
2626
import io.grpc.NameResolver.ConfigOrError;
2727
import io.grpc.Status;
28+
import io.grpc.internal.GrpcUtil;
2829
import io.grpc.internal.JsonUtil;
2930
import io.grpc.util.GracefulSwitchLoadBalancer;
3031
import java.util.LinkedHashMap;
@@ -99,9 +100,10 @@ public ConfigOrError parseLoadBalancingPolicyConfig(Map<String, ?> rawConfig) {
99100
ConfigOrError childConfig = GracefulSwitchLoadBalancer.parseLoadBalancingPolicyConfig(
100101
JsonUtil.getListOfObjects(rawWeightedTarget, "childPolicy"), lbRegistry);
101102
if (childConfig.getError() != null) {
102-
return ConfigOrError.fromError(Status.INTERNAL
103-
.withDescription("Could not parse weighted_target's child policy:" + name)
104-
.withCause(childConfig.getError().asRuntimeException()));
103+
return ConfigOrError.fromError(GrpcUtil.statusWithDetails(
104+
Status.Code.INTERNAL,
105+
"Could not parse weighted_target's child policy: " + name,
106+
childConfig.getError()));
105107
}
106108
parsedChildConfigs.put(name, new WeightedPolicySelection(weight, childConfig.getConfig()));
107109
}

xds/src/main/java/io/grpc/xds/WrrLocalityLoadBalancerProvider.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.grpc.LoadBalancerRegistry;
2424
import io.grpc.NameResolver.ConfigOrError;
2525
import io.grpc.Status;
26+
import io.grpc.internal.GrpcUtil;
2627
import io.grpc.internal.JsonUtil;
2728
import io.grpc.util.GracefulSwitchLoadBalancer;
2829
import io.grpc.xds.WrrLocalityLoadBalancer.WrrLocalityConfig;
@@ -62,9 +63,10 @@ public ConfigOrError parseLoadBalancingPolicyConfig(Map<String, ?> rawConfig) {
6263
ConfigOrError childConfig = GracefulSwitchLoadBalancer.parseLoadBalancingPolicyConfig(
6364
JsonUtil.getListOfObjects(rawConfig, "childPolicy"));
6465
if (childConfig.getError() != null) {
65-
return ConfigOrError.fromError(Status.INTERNAL
66-
.withDescription("Failed to parse child policy in wrr_locality LB policy: " + rawConfig)
67-
.withCause(childConfig.getError().asRuntimeException()));
66+
return ConfigOrError.fromError(GrpcUtil.statusWithDetails(
67+
Status.Code.INTERNAL,
68+
"Failed to parse child policy in wrr_locality LB policy",
69+
childConfig.getError()));
6870
}
6971
return ConfigOrError.fromConfig(new WrrLocalityConfig(childConfig.getConfig()));
7072
} catch (RuntimeException e) {

0 commit comments

Comments
 (0)