From f195928eaa51e63940255adb676bbf3d226859e6 Mon Sep 17 00:00:00 2001 From: Jin Seop Kim Date: Wed, 10 Jun 2026 13:36:16 -0400 Subject: [PATCH 1/7] fix(bigquery): wrap default ResultRetryAlgorithm to retry transient HTTP errors --- .../cloud/bigquery/BigQueryRetryHelper.java | 28 +++++++++++++++ .../cloud/bigquery/BigQueryImplTest.java | 35 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java index 98adb0b273e1..baab0ca2568f 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java @@ -15,6 +15,7 @@ */ package com.google.cloud.bigquery; +import com.google.api.client.http.HttpResponseException; import com.google.api.core.ApiClock; import com.google.api.gax.retrying.DirectRetryingExecutor; import com.google.api.gax.retrying.ExponentialRetryAlgorithm; @@ -23,6 +24,7 @@ import com.google.api.gax.retrying.RetrySettings; import com.google.api.gax.retrying.RetryingExecutor; import com.google.api.gax.retrying.RetryingFuture; +import com.google.api.gax.retrying.TimedAttemptSettings; import com.google.api.gax.retrying.TimedRetryAlgorithm; import com.google.cloud.RetryHelper; import io.opentelemetry.api.trace.Span; @@ -69,6 +71,9 @@ public static V runWithRetries( // implementation does not use response at all, so ignoring its type is ok. @SuppressWarnings("unchecked") ResultRetryAlgorithm algorithm = (ResultRetryAlgorithm) resultRetryAlgorithm; + if (algorithm == BigQueryBaseService.DEFAULT_BIGQUERY_EXCEPTION_HANDLER) { + algorithm = wrapDefaultAlgorithm(algorithm); + } return run( callable, new ExponentialRetryAlgorithm(retrySettings, clock), @@ -119,6 +124,29 @@ private static V run( return retryingFuture.get(); } + private static ResultRetryAlgorithm wrapDefaultAlgorithm( + ResultRetryAlgorithm defaultAlgorithm) { + return new ResultRetryAlgorithm() { + @Override + public TimedAttemptSettings createNextAttempt( + Throwable previousThrowable, V previousResponse, TimedAttemptSettings previousSettings) { + return defaultAlgorithm.createNextAttempt( + previousThrowable, previousResponse, previousSettings); + } + + @Override + public boolean shouldRetry(Throwable previousThrowable, V previousResponse) { + if (previousThrowable instanceof HttpResponseException) { + int statusCode = ((HttpResponseException) previousThrowable).getStatusCode(); + if (statusCode == 500 || statusCode == 502 || statusCode == 503 || statusCode == 504) { + return true; + } + } + return defaultAlgorithm.shouldRetry(previousThrowable, previousResponse); + } + }; + } + public static class BigQueryRetryHelperException extends RuntimeException { private static final long serialVersionUID = -8519852520090965314L; 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( From 4a1bcdbeef22b3cfc84fd1789fc805eec07f24f7 Mon Sep 17 00:00:00 2001 From: Jin Seop Kim Date: Fri, 12 Jun 2026 11:39:33 -0400 Subject: [PATCH 2/7] fix(bigquery): selectively apply HTTP error retries to safe/idempotent RPCs --- .../google/cloud/bigquery/BigQueryImpl.java | 57 +++++++++++-------- .../cloud/bigquery/BigQueryRetryHelper.java | 10 +++- 2 files changed, 39 insertions(+), 28 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..8149c213043e 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 @@ -23,6 +23,7 @@ import com.google.api.core.BetaApi; import com.google.api.core.InternalApi; import com.google.api.gax.paging.Page; +import com.google.api.gax.retrying.ResultRetryAlgorithm; import com.google.api.services.bigquery.model.ErrorProto; import com.google.api.services.bigquery.model.GetQueryResultsResponse; import com.google.api.services.bigquery.model.QueryRequest; @@ -592,7 +593,7 @@ public com.google.api.services.bigquery.model.Dataset call() throws IOException } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -658,7 +659,7 @@ private static Page listDatasets( } }, serviceOptions.getRetrySettings(), - serviceOptions.getResultRetryAlgorithm(), + BigQueryRetryHelper.getHttpRetryAlgorithm(serviceOptions.getResultRetryAlgorithm()), serviceOptions.getClock(), EMPTY_RETRY_CONFIG, serviceOptions.isOpenTelemetryTracingEnabled(), @@ -710,7 +711,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -761,7 +762,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -807,7 +808,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -853,7 +854,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -897,7 +898,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -938,7 +939,7 @@ public com.google.api.services.bigquery.model.Dataset call() throws IOException } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -985,7 +986,7 @@ public com.google.api.services.bigquery.model.Table call() throws IOException { } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1031,7 +1032,7 @@ public com.google.api.services.bigquery.model.Model call() throws IOException { } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1077,7 +1078,7 @@ public com.google.api.services.bigquery.model.Routine call() throws IOException } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1131,7 +1132,7 @@ public com.google.api.services.bigquery.model.Table call() throws IOException { } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1190,7 +1191,7 @@ public com.google.api.services.bigquery.model.Model call() throws IOException { } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1249,7 +1250,7 @@ public com.google.api.services.bigquery.model.Routine call() throws IOException } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1467,7 +1468,7 @@ public Tuple> cal } }, serviceOptions.getRetrySettings(), - serviceOptions.getResultRetryAlgorithm(), + BigQueryRetryHelper.getHttpRetryAlgorithm(serviceOptions.getResultRetryAlgorithm()), serviceOptions.getClock(), EMPTY_RETRY_CONFIG, serviceOptions.isOpenTelemetryTracingEnabled(), @@ -1508,7 +1509,7 @@ public Tuple> cal } }, serviceOptions.getRetrySettings(), - serviceOptions.getResultRetryAlgorithm(), + BigQueryRetryHelper.getHttpRetryAlgorithm(serviceOptions.getResultRetryAlgorithm()), serviceOptions.getClock(), EMPTY_RETRY_CONFIG, serviceOptions.isOpenTelemetryTracingEnabled(), @@ -1549,7 +1550,7 @@ private static Page listRoutines( } }, serviceOptions.getRetrySettings(), - serviceOptions.getResultRetryAlgorithm(), + BigQueryRetryHelper.getHttpRetryAlgorithm(serviceOptions.getResultRetryAlgorithm()), serviceOptions.getClock(), EMPTY_RETRY_CONFIG, serviceOptions.isOpenTelemetryTracingEnabled(), @@ -1631,7 +1632,7 @@ public TableDataInsertAllResponse call() throws Exception { } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1725,7 +1726,7 @@ public TableDataList call() throws IOException { } }, serviceOptions.getRetrySettings(), - serviceOptions.getResultRetryAlgorithm(), + BigQueryRetryHelper.getHttpRetryAlgorithm(serviceOptions.getResultRetryAlgorithm()), serviceOptions.getClock(), EMPTY_RETRY_CONFIG, serviceOptions.isOpenTelemetryTracingEnabled(), @@ -1802,7 +1803,7 @@ public com.google.api.services.bigquery.model.Job call() throws IOException { } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1859,7 +1860,7 @@ public Tuple> call( } }, serviceOptions.getRetrySettings(), - serviceOptions.getResultRetryAlgorithm(), + BigQueryRetryHelper.getHttpRetryAlgorithm(serviceOptions.getResultRetryAlgorithm()), serviceOptions.getClock(), EMPTY_RETRY_CONFIG, serviceOptions.isOpenTelemetryTracingEnabled(), @@ -1914,7 +1915,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -2169,7 +2170,7 @@ public GetQueryResultsResponse call() throws IOException { } }, serviceOptions.getRetrySettings(), - serviceOptions.getResultRetryAlgorithm(), + BigQueryRetryHelper.getHttpRetryAlgorithm(serviceOptions.getResultRetryAlgorithm()), serviceOptions.getClock(), DEFAULT_RETRY_CONFIG, serviceOptions.isOpenTelemetryTracingEnabled(), @@ -2240,7 +2241,7 @@ public com.google.api.services.bigquery.model.Policy call() throws IOException { } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -2334,7 +2335,7 @@ public com.google.api.services.bigquery.model.TestIamPermissionsResponse call() } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getHttpRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -2411,4 +2412,10 @@ private static boolean isRetryErrorCodeHttpNotFound(BigQueryRetryHelperException } return false; } + + @SuppressWarnings("unchecked") + private ResultRetryAlgorithm getHttpRetryAlgorithm() { + return BigQueryRetryHelper.getHttpRetryAlgorithm( + (ResultRetryAlgorithm) getOptions().getResultRetryAlgorithm()); + } } diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java index baab0ca2568f..6482efc8fab9 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java @@ -71,9 +71,6 @@ public static V runWithRetries( // implementation does not use response at all, so ignoring its type is ok. @SuppressWarnings("unchecked") ResultRetryAlgorithm algorithm = (ResultRetryAlgorithm) resultRetryAlgorithm; - if (algorithm == BigQueryBaseService.DEFAULT_BIGQUERY_EXCEPTION_HANDLER) { - algorithm = wrapDefaultAlgorithm(algorithm); - } return run( callable, new ExponentialRetryAlgorithm(retrySettings, clock), @@ -124,6 +121,13 @@ private static V run( return retryingFuture.get(); } + static ResultRetryAlgorithm getHttpRetryAlgorithm(ResultRetryAlgorithm algorithm) { + if (algorithm == BigQueryBaseService.DEFAULT_BIGQUERY_EXCEPTION_HANDLER) { + return wrapDefaultAlgorithm(algorithm); + } + return algorithm; + } + private static ResultRetryAlgorithm wrapDefaultAlgorithm( ResultRetryAlgorithm defaultAlgorithm) { return new ResultRetryAlgorithm() { From 7aab43a1e5e27b5b40a2dbee60150381c67f25b6 Mon Sep 17 00:00:00 2001 From: Jin Seop Kim Date: Fri, 12 Jun 2026 11:50:30 -0400 Subject: [PATCH 3/7] refactor(bigquery): rename retry-related helper methods to reflect conditional wrapping --- .../google/cloud/bigquery/BigQueryImpl.java | 54 +++++++++---------- .../cloud/bigquery/BigQueryRetryHelper.java | 2 +- 2 files changed, 28 insertions(+), 28 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 8149c213043e..755423af5da1 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 @@ -593,7 +593,7 @@ public com.google.api.services.bigquery.model.Dataset call() throws IOException } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -659,7 +659,7 @@ private static Page listDatasets( } }, serviceOptions.getRetrySettings(), - BigQueryRetryHelper.getHttpRetryAlgorithm(serviceOptions.getResultRetryAlgorithm()), + BigQueryRetryHelper.maybeWrapForHttpRetry(serviceOptions.getResultRetryAlgorithm()), serviceOptions.getClock(), EMPTY_RETRY_CONFIG, serviceOptions.isOpenTelemetryTracingEnabled(), @@ -711,7 +711,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -762,7 +762,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -808,7 +808,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -854,7 +854,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -898,7 +898,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -939,7 +939,7 @@ public com.google.api.services.bigquery.model.Dataset call() throws IOException } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -986,7 +986,7 @@ public com.google.api.services.bigquery.model.Table call() throws IOException { } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1032,7 +1032,7 @@ public com.google.api.services.bigquery.model.Model call() throws IOException { } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1078,7 +1078,7 @@ public com.google.api.services.bigquery.model.Routine call() throws IOException } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1132,7 +1132,7 @@ public com.google.api.services.bigquery.model.Table call() throws IOException { } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1191,7 +1191,7 @@ public com.google.api.services.bigquery.model.Model call() throws IOException { } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1250,7 +1250,7 @@ public com.google.api.services.bigquery.model.Routine call() throws IOException } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1468,7 +1468,7 @@ public Tuple> cal } }, serviceOptions.getRetrySettings(), - BigQueryRetryHelper.getHttpRetryAlgorithm(serviceOptions.getResultRetryAlgorithm()), + BigQueryRetryHelper.maybeWrapForHttpRetry(serviceOptions.getResultRetryAlgorithm()), serviceOptions.getClock(), EMPTY_RETRY_CONFIG, serviceOptions.isOpenTelemetryTracingEnabled(), @@ -1509,7 +1509,7 @@ public Tuple> cal } }, serviceOptions.getRetrySettings(), - BigQueryRetryHelper.getHttpRetryAlgorithm(serviceOptions.getResultRetryAlgorithm()), + BigQueryRetryHelper.maybeWrapForHttpRetry(serviceOptions.getResultRetryAlgorithm()), serviceOptions.getClock(), EMPTY_RETRY_CONFIG, serviceOptions.isOpenTelemetryTracingEnabled(), @@ -1550,7 +1550,7 @@ private static Page listRoutines( } }, serviceOptions.getRetrySettings(), - BigQueryRetryHelper.getHttpRetryAlgorithm(serviceOptions.getResultRetryAlgorithm()), + BigQueryRetryHelper.maybeWrapForHttpRetry(serviceOptions.getResultRetryAlgorithm()), serviceOptions.getClock(), EMPTY_RETRY_CONFIG, serviceOptions.isOpenTelemetryTracingEnabled(), @@ -1632,7 +1632,7 @@ public TableDataInsertAllResponse call() throws Exception { } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1726,7 +1726,7 @@ public TableDataList call() throws IOException { } }, serviceOptions.getRetrySettings(), - BigQueryRetryHelper.getHttpRetryAlgorithm(serviceOptions.getResultRetryAlgorithm()), + BigQueryRetryHelper.maybeWrapForHttpRetry(serviceOptions.getResultRetryAlgorithm()), serviceOptions.getClock(), EMPTY_RETRY_CONFIG, serviceOptions.isOpenTelemetryTracingEnabled(), @@ -1803,7 +1803,7 @@ public com.google.api.services.bigquery.model.Job call() throws IOException { } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1860,7 +1860,7 @@ public Tuple> call( } }, serviceOptions.getRetrySettings(), - BigQueryRetryHelper.getHttpRetryAlgorithm(serviceOptions.getResultRetryAlgorithm()), + BigQueryRetryHelper.maybeWrapForHttpRetry(serviceOptions.getResultRetryAlgorithm()), serviceOptions.getClock(), EMPTY_RETRY_CONFIG, serviceOptions.isOpenTelemetryTracingEnabled(), @@ -1915,7 +1915,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -2170,7 +2170,7 @@ public GetQueryResultsResponse call() throws IOException { } }, serviceOptions.getRetrySettings(), - BigQueryRetryHelper.getHttpRetryAlgorithm(serviceOptions.getResultRetryAlgorithm()), + BigQueryRetryHelper.maybeWrapForHttpRetry(serviceOptions.getResultRetryAlgorithm()), serviceOptions.getClock(), DEFAULT_RETRY_CONFIG, serviceOptions.isOpenTelemetryTracingEnabled(), @@ -2241,7 +2241,7 @@ public com.google.api.services.bigquery.model.Policy call() throws IOException { } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -2335,7 +2335,7 @@ public com.google.api.services.bigquery.model.TestIamPermissionsResponse call() } }, getOptions().getRetrySettings(), - getHttpRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -2414,8 +2414,8 @@ private static boolean isRetryErrorCodeHttpNotFound(BigQueryRetryHelperException } @SuppressWarnings("unchecked") - private ResultRetryAlgorithm getHttpRetryAlgorithm() { - return BigQueryRetryHelper.getHttpRetryAlgorithm( + private ResultRetryAlgorithm getRetryAlgorithmWithHttpRetry() { + return BigQueryRetryHelper.maybeWrapForHttpRetry( (ResultRetryAlgorithm) getOptions().getResultRetryAlgorithm()); } } diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java index 6482efc8fab9..1f4631899838 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java @@ -121,7 +121,7 @@ private static V run( return retryingFuture.get(); } - static ResultRetryAlgorithm getHttpRetryAlgorithm(ResultRetryAlgorithm algorithm) { + static ResultRetryAlgorithm maybeWrapForHttpRetry(ResultRetryAlgorithm algorithm) { if (algorithm == BigQueryBaseService.DEFAULT_BIGQUERY_EXCEPTION_HANDLER) { return wrapDefaultAlgorithm(algorithm); } From 9845a87bad6982de0351e240ee48eec87339cbc8 Mon Sep 17 00:00:00 2001 From: Jin Seop Kim Date: Fri, 12 Jun 2026 12:08:54 -0400 Subject: [PATCH 4/7] fix(bigquery): limit HTTP retries to safe operations and enable for createRoutine --- .../google/cloud/bigquery/BigQueryImpl.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 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 755423af5da1..cdb71d54fb58 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 @@ -397,7 +397,7 @@ public com.google.api.services.bigquery.model.Routine call() throws IOException } }, getOptions().getRetrySettings(), - getOptions().getResultRetryAlgorithm(), + getRetryAlgorithmWithHttpRetry(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -711,7 +711,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getRetryAlgorithmWithHttpRetry(), + getOptions().getResultRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -762,7 +762,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getRetryAlgorithmWithHttpRetry(), + getOptions().getResultRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -808,7 +808,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getRetryAlgorithmWithHttpRetry(), + getOptions().getResultRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -854,7 +854,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getRetryAlgorithmWithHttpRetry(), + getOptions().getResultRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -898,7 +898,7 @@ public Boolean call() throws IOException { } }, getOptions().getRetrySettings(), - getRetryAlgorithmWithHttpRetry(), + getOptions().getResultRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -939,7 +939,7 @@ public com.google.api.services.bigquery.model.Dataset call() throws IOException } }, getOptions().getRetrySettings(), - getRetryAlgorithmWithHttpRetry(), + getOptions().getResultRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -986,7 +986,7 @@ public com.google.api.services.bigquery.model.Table call() throws IOException { } }, getOptions().getRetrySettings(), - getRetryAlgorithmWithHttpRetry(), + getOptions().getResultRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1032,7 +1032,7 @@ public com.google.api.services.bigquery.model.Model call() throws IOException { } }, getOptions().getRetrySettings(), - getRetryAlgorithmWithHttpRetry(), + getOptions().getResultRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1078,7 +1078,7 @@ public com.google.api.services.bigquery.model.Routine call() throws IOException } }, getOptions().getRetrySettings(), - getRetryAlgorithmWithHttpRetry(), + getOptions().getResultRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), @@ -1632,7 +1632,7 @@ public TableDataInsertAllResponse call() throws Exception { } }, getOptions().getRetrySettings(), - getRetryAlgorithmWithHttpRetry(), + getOptions().getResultRetryAlgorithm(), getOptions().getClock(), EMPTY_RETRY_CONFIG, getOptions().isOpenTelemetryTracingEnabled(), From 337e8b1faede2b0b2b3bdd14a12f58c016295cb3 Mon Sep 17 00:00:00 2001 From: Jin Seop Kim Date: Fri, 12 Jun 2026 12:18:14 -0400 Subject: [PATCH 5/7] docs(bigquery): add javadoc comments to retry-wrapping helper methods --- .../main/java/com/google/cloud/bigquery/BigQueryImpl.java | 6 ++++++ .../com/google/cloud/bigquery/BigQueryRetryHelper.java | 8 ++++++++ 2 files changed, 14 insertions(+) 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 cdb71d54fb58..9d040b4f12aa 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 @@ -2413,6 +2413,12 @@ private static boolean isRetryErrorCodeHttpNotFound(BigQueryRetryHelperException return false; } + /** + * Helper to retrieve the retry algorithm wrapped for safe HTTP 5xx error retries. + * + *

This delegates to {@link BigQueryRetryHelper#maybeWrapForHttpRetry} to ensure safe + * conditional wrapping of the default algorithm while leaving custom user algorithms untouched. + */ @SuppressWarnings("unchecked") private ResultRetryAlgorithm getRetryAlgorithmWithHttpRetry() { return BigQueryRetryHelper.maybeWrapForHttpRetry( diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java index 1f4631899838..335310701087 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java @@ -121,6 +121,14 @@ private static V run( return retryingFuture.get(); } + /** + * Conditionally wraps the provided retry algorithm with a wrapper that retries on transient HTTP + * 5xx errors (500, 502, 503, 504). + * + *

Wrapping only occurs if the provided algorithm is the default {@link + * BigQueryBaseService#DEFAULT_BIGQUERY_EXCEPTION_HANDLER}. Custom user-defined retry algorithms + * are returned unmodified to preserve custom retry policies. + */ static ResultRetryAlgorithm maybeWrapForHttpRetry(ResultRetryAlgorithm algorithm) { if (algorithm == BigQueryBaseService.DEFAULT_BIGQUERY_EXCEPTION_HANDLER) { return wrapDefaultAlgorithm(algorithm); From fdcde0a87b0919e2fc9150f43cb6ddfaf152112e Mon Sep 17 00:00:00 2001 From: Jin Seop Kim Date: Fri, 12 Jun 2026 12:21:56 -0400 Subject: [PATCH 6/7] docs(bigquery): update retry helper comments as suggested --- .../src/main/java/com/google/cloud/bigquery/BigQueryImpl.java | 2 +- .../java/com/google/cloud/bigquery/BigQueryRetryHelper.java | 2 +- 2 files changed, 2 insertions(+), 2 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 9d040b4f12aa..a36136419824 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 @@ -2414,7 +2414,7 @@ private static boolean isRetryErrorCodeHttpNotFound(BigQueryRetryHelperException } /** - * Helper to retrieve the retry algorithm wrapped for safe HTTP 5xx error retries. + * Helper to retrieve the retry algorithm wrapped for HTTP error retries. * *

This delegates to {@link BigQueryRetryHelper#maybeWrapForHttpRetry} to ensure safe * conditional wrapping of the default algorithm while leaving custom user algorithms untouched. diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java index 335310701087..263dc1699cb6 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java @@ -123,7 +123,7 @@ private static V run( /** * Conditionally wraps the provided retry algorithm with a wrapper that retries on transient HTTP - * 5xx errors (500, 502, 503, 504). + * errors. * *

Wrapping only occurs if the provided algorithm is the default {@link * BigQueryBaseService#DEFAULT_BIGQUERY_EXCEPTION_HANDLER}. Custom user-defined retry algorithms From dc01c5e3c48213660b07bb25cfb0e71da86783a6 Mon Sep 17 00:00:00 2001 From: Jin Seop Kim Date: Fri, 12 Jun 2026 12:22:35 -0400 Subject: [PATCH 7/7] docs(bigquery): add javadoc comment to wrapDefaultAlgorithm --- .../java/com/google/cloud/bigquery/BigQueryRetryHelper.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java index 263dc1699cb6..089e7cd78c33 100644 --- a/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java +++ b/java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryHelper.java @@ -136,6 +136,11 @@ static ResultRetryAlgorithm maybeWrapForHttpRetry(ResultRetryAlgorithm return algorithm; } + /** + * Wraps the default retry algorithm to additionally retry on transient HTTP status codes 500, + * 502, 503, and 504. Other retry decisions and timing logic are delegated back to the default + * algorithm. + */ private static ResultRetryAlgorithm wrapDefaultAlgorithm( ResultRetryAlgorithm defaultAlgorithm) { return new ResultRetryAlgorithm() {