From a11da9f4014b83a006cad732d2da272638f0b9c3 Mon Sep 17 00:00:00 2001 From: Goutam Adwant Date: Sat, 6 Jun 2026 13:52:43 -0700 Subject: [PATCH] fix(bigquery): retry getTable server errors Translate HTTP response exceptions thrown by getTable before retry evaluation so existing BigQueryException retryability handles transient server errors. Keep the change scoped to getTable and leave raw network IOExceptions on the existing retry path. Fixes #12885 --- .../google/cloud/bigquery/BigQueryImpl.java | 15 +++++--- .../cloud/bigquery/BigQueryImplTest.java | 35 +++++++++++++++++++ 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java index 74c9ce60e84f..b02083d81231 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java @@ -20,6 +20,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static java.net.HttpURLConnection.HTTP_NOT_FOUND; +import com.google.api.client.http.HttpResponseException; import com.google.api.core.BetaApi; import com.google.api.core.InternalApi; import com.google.api.gax.paging.Page; @@ -1123,11 +1124,15 @@ && getOptions().getOpenTelemetryTracer() != null) { new Callable() { @Override public com.google.api.services.bigquery.model.Table call() throws IOException { - return bigQueryRpc.getTableSkipExceptionTranslation( - completeTableId.getProject(), - completeTableId.getDataset(), - completeTableId.getTable(), - optionsMap); + try { + return bigQueryRpc.getTableSkipExceptionTranslation( + completeTableId.getProject(), + completeTableId.getDataset(), + completeTableId.getTable(), + optionsMap); + } catch (HttpResponseException e) { + throw new BigQueryException(e); + } } }, getOptions().getRetrySettings(), diff --git a/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java b/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java index 20a6ef679e89..7fea041b4025 100644 --- a/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java +++ b/java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java @@ -36,6 +36,10 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.google.api.client.googleapis.json.GoogleJsonError; +import com.google.api.client.googleapis.json.GoogleJsonResponseException; +import com.google.api.client.http.HttpHeaders; +import com.google.api.client.http.HttpResponseException; import com.google.api.gax.paging.Page; import com.google.api.services.bigquery.model.ErrorProto; import com.google.api.services.bigquery.model.GetQueryResultsResponse; @@ -935,6 +939,37 @@ void testGetTable() throws IOException { .getTableSkipExceptionTranslation(PROJECT, DATASET, TABLE, EMPTY_RPC_OPTIONS); } + @Test + void testGetTableFailureShouldRetryServerErrors() throws IOException { + GoogleJsonError error = new GoogleJsonError(); + error.setMessage("Visibility check was unavailable. Please retry the request"); + error.setCode(503); + GoogleJsonError.ErrorInfo errorInfo = new GoogleJsonError.ErrorInfo(); + errorInfo.setReason("backendError"); + error.setErrors(ImmutableList.of(errorInfo)); + + when(bigqueryRpcMock.getTableSkipExceptionTranslation( + PROJECT, DATASET, TABLE, EMPTY_RPC_OPTIONS)) + .thenThrow(new GoogleJsonResponseException(serverErrorResponse(), error)) + .thenReturn(TABLE_INFO_WITH_PROJECT.toPb()); + + bigquery = + options.toBuilder() + .setRetrySettings(ServiceOptions.getDefaultRetrySettings()) + .build() + .getService(); + + Table table = bigquery.getTable(DATASET, TABLE); + + assertEquals(new Table(bigquery, new TableInfo.BuilderImpl(TABLE_INFO_WITH_PROJECT)), table); + verify(bigqueryRpcMock, times(2)) + .getTableSkipExceptionTranslation(PROJECT, DATASET, TABLE, EMPTY_RPC_OPTIONS); + } + + private static HttpResponseException.Builder serverErrorResponse() { + return new HttpResponseException.Builder(503, "Service Unavailable", new HttpHeaders()); + } + @Test void testGetModel() throws IOException { when(bigqueryRpcMock.getModelSkipExceptionTranslation(