Skip to content

Commit dd67534

Browse files
fix: More robust STREAM_RST logic (#1102)
* fix: more robust STREAM_RST logic * Update google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/util/Errors.java Co-authored-by: Stephanie Wang <stephaniewang526@users.noreply.github.com> * Update google-cloud-bigquerystorage/src/test/java/com/google/cloud/bigquery/storage/util/ErrorsTest.java Co-authored-by: Stephanie Wang <stephaniewang526@users.noreply.github.com> Co-authored-by: Stephanie Wang <stephaniewang526@users.noreply.github.com>
1 parent b2e3489 commit dd67534

File tree

5 files changed

+103
-27
lines changed

5 files changed

+103
-27
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2021 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.bigquery.storage.util;
17+
18+
import io.grpc.Status;
19+
20+
/** Static utility methods for working with Errors returned from the service. */
21+
public class Errors {
22+
private Errors() {};
23+
24+
/**
25+
* Returns true iff the Status indicates and internal error that is retryable.
26+
*
27+
* <p>Generally, internal errors are not considered retryable, however there are certain transient
28+
* network issues that appear as internal but are in fact retryable.
29+
*/
30+
public static boolean isRetryableInternalStatus(Status status) {
31+
String description = status.getDescription();
32+
return status.getCode() == Status.Code.INTERNAL
33+
&& description != null
34+
&& (description.contains("Received unexpected EOS on DATA frame from server")
35+
|| description.contains(" Rst ")
36+
|| description.contains("RST_STREAM")
37+
|| description.contains("Connection closed with unknown cause")
38+
|| description.contains("HTTP/2 error code: INTERNAL_ERROR"));
39+
}
40+
}

google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1/stub/readrows/ApiResultRetryAlgorithm.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.api.gax.retrying.ResultRetryAlgorithm;
2121
import com.google.api.gax.retrying.TimedAttemptSettings;
2222
import com.google.api.gax.rpc.ApiException;
23+
import com.google.cloud.bigquery.storage.util.Errors;
2324
import io.grpc.Status;
2425
import org.threeten.bp.Duration;
2526

@@ -29,19 +30,12 @@ public class ApiResultRetryAlgorithm<ResponseT> implements ResultRetryAlgorithm<
2930
// Duration to sleep on if the error is DEADLINE_EXCEEDED.
3031
public static final Duration DEADLINE_SLEEP_DURATION = Duration.ofMillis(1);
3132

32-
private boolean isRetryableStatus(Status status) {
33-
return status.getCode() == Status.Code.INTERNAL
34-
&& status.getDescription() != null
35-
&& (status.getDescription().contains("Received unexpected EOS on DATA frame from server")
36-
|| status.getDescription().contains("Received Rst Stream"));
37-
}
38-
3933
@Override
4034
public TimedAttemptSettings createNextAttempt(
4135
Throwable prevThrowable, ResponseT prevResponse, TimedAttemptSettings prevSettings) {
4236
if (prevThrowable != null) {
4337
Status status = Status.fromThrowable(prevThrowable);
44-
if (isRetryableStatus(status)) {
38+
if (Errors.isRetryableInternalStatus(status)) {
4539
return TimedAttemptSettings.newBuilder()
4640
.setGlobalSettings(prevSettings.getGlobalSettings())
4741
.setRetryDelay(prevSettings.getRetryDelay())
@@ -59,7 +53,7 @@ public TimedAttemptSettings createNextAttempt(
5953
public boolean shouldRetry(Throwable prevThrowable, ResponseT prevResponse) {
6054
if (prevThrowable != null) {
6155
Status status = Status.fromThrowable(prevThrowable);
62-
if (isRetryableStatus(status)) {
56+
if (Errors.isRetryableInternalStatus(status)) {
6357
return true;
6458
}
6559
}

google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1beta1/stub/readrows/ApiResultRetryAlgorithm.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.api.gax.retrying.ResultRetryAlgorithm;
2121
import com.google.api.gax.retrying.TimedAttemptSettings;
2222
import com.google.api.gax.rpc.ApiException;
23+
import com.google.cloud.bigquery.storage.util.Errors;
2324
import io.grpc.Status;
2425
import org.threeten.bp.Duration;
2526

@@ -29,19 +30,12 @@ public class ApiResultRetryAlgorithm<ResponseT> implements ResultRetryAlgorithm<
2930
// Duration to sleep on if the error is DEADLINE_EXCEEDED.
3031
public static final Duration DEADLINE_SLEEP_DURATION = Duration.ofMillis(1);
3132

32-
private boolean isRetryableStatus(Status status) {
33-
return status.getCode() == Status.Code.INTERNAL
34-
&& status.getDescription() != null
35-
&& (status.getDescription().contains("Received unexpected EOS on DATA frame from server")
36-
|| status.getDescription().contains("Received Rst Stream"));
37-
}
38-
3933
@Override
4034
public TimedAttemptSettings createNextAttempt(
4135
Throwable prevThrowable, ResponseT prevResponse, TimedAttemptSettings prevSettings) {
4236
if (prevThrowable != null) {
4337
Status status = Status.fromThrowable(prevThrowable);
44-
if (isRetryableStatus(status)) {
38+
if (Errors.isRetryableInternalStatus(status)) {
4539
return TimedAttemptSettings.newBuilder()
4640
.setGlobalSettings(prevSettings.getGlobalSettings())
4741
.setRetryDelay(prevSettings.getRetryDelay())
@@ -59,7 +53,7 @@ public TimedAttemptSettings createNextAttempt(
5953
public boolean shouldRetry(Throwable prevThrowable, ResponseT prevResponse) {
6054
if (prevThrowable != null) {
6155
Status status = Status.fromThrowable(prevThrowable);
62-
if (isRetryableStatus(status)) {
56+
if (Errors.isRetryableInternalStatus(status)) {
6357
return true;
6458
}
6559
}

google-cloud-bigquerystorage/src/main/java/com/google/cloud/bigquery/storage/v1beta2/stub/readrows/ApiResultRetryAlgorithm.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.api.gax.retrying.ResultRetryAlgorithm;
2121
import com.google.api.gax.retrying.TimedAttemptSettings;
2222
import com.google.api.gax.rpc.ApiException;
23+
import com.google.cloud.bigquery.storage.util.Errors;
2324
import io.grpc.Status;
2425
import org.threeten.bp.Duration;
2526

@@ -29,19 +30,12 @@ public class ApiResultRetryAlgorithm<ResponseT> implements ResultRetryAlgorithm<
2930
// Duration to sleep on if the error is DEADLINE_EXCEEDED.
3031
public static final Duration DEADLINE_SLEEP_DURATION = Duration.ofMillis(1);
3132

32-
private boolean isRetryableStatus(Status status) {
33-
return status.getCode() == Status.Code.INTERNAL
34-
&& status.getDescription() != null
35-
&& (status.getDescription().contains("Received unexpected EOS on DATA frame from server")
36-
|| status.getDescription().contains("Received Rst Stream"));
37-
}
38-
3933
@Override
4034
public TimedAttemptSettings createNextAttempt(
4135
Throwable prevThrowable, ResponseT prevResponse, TimedAttemptSettings prevSettings) {
4236
if (prevThrowable != null) {
4337
Status status = Status.fromThrowable(prevThrowable);
44-
if (isRetryableStatus(status)) {
38+
if (Errors.isRetryableInternalStatus(status)) {
4539
return TimedAttemptSettings.newBuilder()
4640
.setGlobalSettings(prevSettings.getGlobalSettings())
4741
.setRetryDelay(prevSettings.getRetryDelay())
@@ -59,7 +53,7 @@ public TimedAttemptSettings createNextAttempt(
5953
public boolean shouldRetry(Throwable prevThrowable, ResponseT prevResponse) {
6054
if (prevThrowable != null) {
6155
Status status = Status.fromThrowable(prevThrowable);
62-
if (isRetryableStatus(status)) {
56+
if (Errors.isRetryableInternalStatus(status)) {
6357
return true;
6458
}
6559
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2021 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.bigquery.storage.util;
17+
18+
import static org.junit.Assert.assertFalse;
19+
import static org.junit.Assert.assertTrue;
20+
21+
import io.grpc.Status;
22+
import org.junit.Test;
23+
import org.junit.runner.RunWith;
24+
import org.junit.runners.JUnit4;
25+
26+
@RunWith(JUnit4.class)
27+
public class ErrorsTest {
28+
29+
@Test
30+
public void testRetryableInternalForRstErrors() {
31+
assertTrue(
32+
Errors.isRetryableInternalStatus(
33+
Status.INTERNAL.withDescription(
34+
"HTTP/2 error code: INTERNAL_ERROR\nReceived Rst stream")));
35+
assertTrue(
36+
Errors.isRetryableInternalStatus(
37+
Status.INTERNAL.withDescription(
38+
"RST_STREAM closed stream. HTTP/2 error code: INTERNAL_ERROR")));
39+
}
40+
41+
@Test
42+
public void testNonRetryableInternalError() {
43+
assertFalse(Errors.isRetryableInternalStatus(Status.INTERNAL));
44+
assertFalse(Errors.isRetryableInternalStatus(Status.INTERNAL.withDescription("Server error.")));
45+
}
46+
47+
@Test
48+
public void testNonRetryableOtherError() {
49+
assertFalse(
50+
Errors.isRetryableInternalStatus(
51+
Status.DATA_LOSS.withDescription(
52+
"RST_STREAM closed stream. HTTP/2 error code: INTERNAL_ERROR")));
53+
}
54+
}

0 commit comments

Comments
 (0)