| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent e4c11c0 commit 1835cd7
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -16,10 +16,9 @@ | |||
| 16 | 16 | ||
| 17 | 17 | package com.google.cloud.spanner; | |
| 18 | 18 | ||
| 19 | - import static com.google.cloud.spanner.SpannerException.DoNotConstructDirectly; | ||
| 20 | - | ||
| 21 | 19 | import com.google.api.gax.grpc.GrpcStatusCode; | |
| 22 | 20 | import com.google.api.gax.rpc.ApiException; | |
| 21 | + import com.google.cloud.spanner.SpannerException.DoNotConstructDirectly; | ||
| 23 | 22 | import com.google.common.base.MoreObjects; | |
| 24 | 23 | import com.google.common.base.Predicate; | |
| 25 | 24 | import io.grpc.Context; | |
@@ -28,6 +27,7 @@ | |||
| 28 | 27 | import java.util.concurrent.CancellationException; | |
| 29 | 28 | import java.util.concurrent.TimeoutException; | |
| 30 | 29 | import javax.annotation.Nullable; | |
| 30 | + import javax.net.ssl.SSLHandshakeException; | ||
| 31 | 31 | ||
| 32 | 32 | /** | |
| 33 | 33 | * A factory for creating instances of {@link SpannerException} and its subtypes. All creation of | |
@@ -168,7 +168,9 @@ private static boolean isRetryable(ErrorCode code, @Nullable Throwable cause) { | |||
| 168 | 168 | case INTERNAL: | |
| 169 | 169 | return hasCauseMatching(cause, Matchers.isRetryableInternalError); | |
| 170 | 170 | case UNAVAILABLE: | |
| 171 | - return true; | ||
| 171 | + // SSLHandshakeException is (probably) not retryable, as it is an indication that the server | ||
| 172 | + // certificate was not accepted by the client. | ||
| 173 | + return !hasCauseMatching(cause, Matchers.isSSLHandshakeException); | ||
| 172 | 174 | case RESOURCE_EXHAUSTED: | |
| 173 | 175 | return SpannerException.extractRetryDelay(cause) > 0; | |
| 174 | 176 | default: | |
@@ -211,5 +213,12 @@ public boolean apply(Throwable cause) { | |||
| 211 | 213 | return false; | |
| 212 | 214 | } | |
| 213 | 215 | }; | |
| 216 | + static final Predicate<Throwable> isSSLHandshakeException = | ||
| 217 | + new Predicate<Throwable>() { | ||
| 218 | + @Override | ||
| 219 | + public boolean apply(Throwable input) { | ||
| 220 | + return input instanceof SSLHandshakeException; | ||
| 221 | + } | ||
| 222 | + }; | ||
| 214 | 223 | } | |
| 215 | 224 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,13 +17,16 @@ | |||
| 17 | 17 | package com.google.cloud.spanner; | |
| 18 | 18 | ||
| 19 | 19 | import static com.google.common.truth.Truth.assertThat; | |
| 20 | + import static org.hamcrest.CoreMatchers.is; | ||
| 21 | + import static org.junit.Assert.assertThat; | ||
| 20 | 22 | import static org.junit.Assert.fail; | |
| 21 | 23 | ||
| 22 | 24 | import com.google.cloud.grpc.GrpcTransportOptions; | |
| 23 | 25 | import com.google.cloud.spanner.spi.v1.SpannerRpc; | |
| 24 | 26 | import java.util.HashMap; | |
| 25 | 27 | import java.util.Map; | |
| 26 | 28 | import java.util.concurrent.Callable; | |
| 29 | + import javax.net.ssl.SSLHandshakeException; | ||
| 27 | 30 | import org.junit.Before; | |
| 28 | 31 | import org.junit.Test; | |
| 29 | 32 | import org.junit.runner.RunWith; | |
@@ -133,4 +136,49 @@ public Void call() throws Exception { | |||
| 133 | 136 | assertThat(e.getMessage().contains("Unexpected exception thrown")); | |
| 134 | 137 | } | |
| 135 | 138 | } | |
| 139 | + | ||
| 140 | + @Test | ||
| 141 | + public void sslHandshakeExceptionIsNotRetryable() { | ||
| 142 | + // Verify that a SpannerException with code UNAVAILABLE and cause SSLHandshakeException is not | ||
| 143 | + // retryable. | ||
| 144 | + boolean gotExpectedException = false; | ||
| 145 | + try { | ||
| 146 | + SpannerImpl.runWithRetries( | ||
| 147 | + new Callable<Object>() { | ||
| 148 | + @Override | ||
| 149 | + public Void call() throws Exception { | ||
| 150 | + throw SpannerExceptionFactory.newSpannerException( | ||
| 151 | + ErrorCode.UNAVAILABLE, | ||
| 152 | + "This exception should not be retryable", | ||
| 153 | + new SSLHandshakeException("some SSL handshake exception")); | ||
| 154 | + } | ||
| 155 | + }); | ||
| 156 | + } catch (SpannerException e) { | ||
| 157 | + gotExpectedException = true; | ||
| 158 | + assertThat(e.isRetryable(), is(false)); | ||
| 159 | + assertThat(e.getErrorCode()).isEqualTo(ErrorCode.UNAVAILABLE); | ||
| 160 | + assertThat(e.getMessage().contains("This exception should not be retryable")); | ||
| 161 | + } | ||
| 162 | + assertThat(gotExpectedException, is(true)); | ||
| 163 | + | ||
| 164 | + // Verify that any other SpannerException with code UNAVAILABLE is retryable. | ||
| 165 | + SpannerImpl.runWithRetries( | ||
| 166 | + new Callable<Object>() { | ||
| 167 | + private boolean firstTime = true; | ||
| 168 | + | ||
| 169 | + @Override | ||
| 170 | + public Void call() throws Exception { | ||
| 171 | + // Keep track of whethr this is the first call or a subsequent call to avoid an infinite | ||
| 172 | + // loop. | ||
| 173 | + if (firstTime) { | ||
| 174 | + firstTime = false; | ||
| 175 | + throw SpannerExceptionFactory.newSpannerException( | ||
| 176 | + ErrorCode.UNAVAILABLE, | ||
| 177 | + "This exception should be retryable", | ||
| 178 | + new Exception("some other exception")); | ||
| 179 | + } | ||
| 180 | + return null; | ||
| 181 | + } | ||
| 182 | + }); | ||
| 183 | + } | ||
| 136 | 184 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments