diff --git a/README.md b/README.md index 4ef41769bb..66380e828e 100644 --- a/README.md +++ b/README.md @@ -49,20 +49,20 @@ If you are using Maven without BOM, add this to your dependencies: If you are using Gradle 5.x or later, add this to your dependencies: ```Groovy -implementation platform('com.google.cloud:libraries-bom:26.5.0') +implementation platform('com.google.cloud:libraries-bom:26.6.0') implementation 'com.google.cloud:google-cloud-bigtable' ``` If you are using Gradle without BOM, add this to your dependencies: ```Groovy -implementation 'com.google.cloud:google-cloud-bigtable:2.18.3' +implementation 'com.google.cloud:google-cloud-bigtable:2.18.4' ``` If you are using SBT, add this to your dependencies: ```Scala -libraryDependencies += "com.google.cloud" % "google-cloud-bigtable" % "2.18.3" +libraryDependencies += "com.google.cloud" % "google-cloud-bigtable" % "2.18.4" ``` ## Authentication diff --git a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/ConvertExceptionCallable.java b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/ConvertExceptionCallable.java index d3ff88af7e..afc517bbc3 100644 --- a/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/ConvertExceptionCallable.java +++ b/google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/data/v2/stub/ConvertExceptionCallable.java @@ -21,6 +21,7 @@ import com.google.api.gax.rpc.ResponseObserver; import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.StreamController; +import com.google.common.base.Throwables; /** * This callable converts the "Received rst stream" exception into a retryable {@link ApiException}. @@ -73,14 +74,29 @@ protected void onCompleteImpl() { } private Throwable convertException(Throwable t) { - // Long lived connections sometimes are disconnected via an RST frame. This error is - // transient and should be retried. + // Long lived connections sometimes are disconnected via an RST frame or a goaway. These errors + // are transient and should be retried. + if (isRstStreamError(t) || isGoAway(t)) { + return new InternalException(t, ((InternalException) t).getStatusCode(), true); + } + return t; + } + + private boolean isRstStreamError(Throwable t) { if (t instanceof InternalException && t.getMessage() != null) { String error = t.getMessage().toLowerCase(); - if (error.contains("rst_stream") || error.contains("rst stream")) { - return new InternalException(t, ((InternalException) t).getStatusCode(), true); - } + return error.contains("rst_stream") || error.contains("rst stream"); } - return t; + return false; + } + + private boolean isGoAway(Throwable t) { + if (t instanceof InternalException) { + Throwable rootCause = Throwables.getRootCause(t); + String rootCauseMessage = rootCause.getMessage(); + return rootCauseMessage != null + && rootCauseMessage.contains("Stream closed before write could take place"); + } + return false; } }