Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 2.4.12 ##
* Core: treat gRPC ResourceExhausted "message larger than max" as non-retryable

## 2.4.11 ##
* Table: fixed StackOverflow problem for the immediately released sessions

Expand Down
21 changes: 17 additions & 4 deletions core/src/main/java/tech/ydb/core/grpc/GrpcStatuses.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static tech.ydb.core.Status toStatus(io.grpc.Status status, String endpoi
return Status.SUCCESS;
}
Issue message = Issue.of(getMessage(status, endpoint), Issue.Severity.ERROR);
StatusCode code = getStatusCode(status.getCode());
StatusCode code = getStatusCode(status);
Throwable cause = status.getCause();

if (cause == null) {
Expand All @@ -49,16 +49,29 @@ private static String getMessage(io.grpc.Status status, String endpoint) {
return message;
}

private static StatusCode getStatusCode(io.grpc.Status.Code code) {
switch (code) {
private static StatusCode getStatusCode(io.grpc.Status status) {
switch (status.getCode()) {
case UNAVAILABLE: return StatusCode.TRANSPORT_UNAVAILABLE;
case UNAUTHENTICATED: return StatusCode.CLIENT_UNAUTHENTICATED;
case CANCELLED: return StatusCode.CLIENT_CANCELLED;
case UNIMPLEMENTED: return StatusCode.CLIENT_CALL_UNIMPLEMENTED;
case DEADLINE_EXCEEDED: return StatusCode.CLIENT_DEADLINE_EXCEEDED;
case RESOURCE_EXHAUSTED: return StatusCode.CLIENT_RESOURCE_EXHAUSTED;
case RESOURCE_EXHAUSTED:
// A message that exceeds the gRPC size limit is a deterministic client-side error:
// retrying it with the same payload always fails identically. Map it to a non-retryable
// status so the caller fails fast instead of spinning through the retry budget.
// Other ResourceExhausted errors (e.g. quota or rate limiting) stay retryable.
if (isMessageLargerThanMax(status.getDescription())) {
return StatusCode.BAD_REQUEST;
}
return StatusCode.CLIENT_RESOURCE_EXHAUSTED;
default:
return StatusCode.CLIENT_GRPC_ERROR;
}
}

private static boolean isMessageLargerThanMax(String description) {
return description != null && (description.contains("trying to send message larger than max")
|| description.contains("received message larger than max"));
}
}
28 changes: 28 additions & 0 deletions core/src/test/java/tech/ydb/core/grpc/GrpcStatusesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,34 @@ public void statusResourceExhausted() {
assertEquals(Status.of(StatusCode.CLIENT_RESOURCE_EXHAUSTED).withIssues(issue), status);
}

@Test
public void statusResourceExhaustedMessageTooLargeSend() {
Status status = GrpcStatuses.toStatus(
io.grpc.Status.RESOURCE_EXHAUSTED.withDescription("trying to send message larger than max"), "test");
Issue issue = Issue.of(
"gRPC error: (RESOURCE_EXHAUSTED) on test, trying to send message larger than max",
Issue.Severity.ERROR);
assertEquals(Status.of(StatusCode.BAD_REQUEST).withIssues(issue), status);
}

@Test
public void statusResourceExhaustedMessageTooLargeReceive() {
Status status = GrpcStatuses.toStatus(
io.grpc.Status.RESOURCE_EXHAUSTED.withDescription("received message larger than max"), "test");
Issue issue = Issue.of(
"gRPC error: (RESOURCE_EXHAUSTED) on test, received message larger than max",
Issue.Severity.ERROR);
assertEquals(Status.of(StatusCode.BAD_REQUEST).withIssues(issue), status);
}

@Test
public void statusResourceExhaustedOtherDescription() {
Status status = GrpcStatuses.toStatus(
io.grpc.Status.RESOURCE_EXHAUSTED.withDescription("quota exceeded"), "test");
Issue issue = Issue.of("gRPC error: (RESOURCE_EXHAUSTED) on test, quota exceeded", Issue.Severity.ERROR);
assertEquals(Status.of(StatusCode.CLIENT_RESOURCE_EXHAUSTED).withIssues(issue), status);
}

@Test
public void statusThrowable() {
Throwable th = new RuntimeException("Hello");
Expand Down