Skip to content

Commit ec68c11

Browse files
authored
feat: Updated BigQueryRetryAlgorithm so that it can retry on RateLimit Errors using RegEx (#1499)
* Modified RATE_LIMIT_ERROR_MSG and added testRateLimitRegEx for testing regex matching * Added JOB_RATE_LIMIT_EXCEEDED_MSG and RATE_LIMIT_EXCEEDED_REGEX pattern * Implemented retryOnRegEx to include retry on RegEx patterns * Updated DEFAULT_RETRY_CONFIG to include retry on JOB_RATE_LIMIT_EXCEEDED_MSG and RATE_LIMIT_EXCEEDED_REGEX * Modified shouldRetryBasedOnBigQueryRetryConfig method to try RegEx matching if all the contains checks fail
1 parent b705052 commit ec68c11

File tree

5 files changed

+69
-6
lines changed

5 files changed

+69
-6
lines changed

google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryErrorMessages.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,9 @@
1919
public class BigQueryErrorMessages {
2020
public static final String RATE_LIMIT_EXCEEDED_MSG =
2121
"Exceeded rate limits:"; // Error Message for RateLimitExceeded Error
22+
public static final String JOB_RATE_LIMIT_EXCEEDED_MSG = "Job exceeded rate limits:";
23+
24+
public class RetryRegExPatterns {
25+
public static final String RATE_LIMIT_EXCEEDED_REGEX = ".*exceed.*rate.*limit.*";
26+
}
2227
}

google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,9 @@ public Page<FieldValueList> getNextPage() {
240240
private static final BigQueryRetryConfig DEFAULT_RETRY_CONFIG =
241241
BigQueryRetryConfig.newBuilder()
242242
.retryOnMessage(BigQueryErrorMessages.RATE_LIMIT_EXCEEDED_MSG)
243-
.build(); // retry config with Error Message for RateLimitExceeded Error
243+
.retryOnMessage(BigQueryErrorMessages.JOB_RATE_LIMIT_EXCEEDED_MSG)
244+
.retryOnRegEx(BigQueryErrorMessages.RetryRegExPatterns.RATE_LIMIT_EXCEEDED_REGEX)
245+
.build(); // retry config with Error Messages and RegEx for RateLimitExceeded Error
244246

245247
BigQueryImpl(BigQueryOptions options) {
246248
super(options);

google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryAlgorithm.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.google.api.gax.retrying.TimedRetryAlgorithmWithContext;
2828
import java.util.Iterator;
2929
import java.util.concurrent.CancellationException;
30+
import java.util.regex.Pattern;
3031

3132
public class BigQueryRetryAlgorithm<ResponseT> extends RetryAlgorithm<ResponseT> {
3233
private final BigQueryRetryConfig bigQueryRetryConfig;
@@ -69,17 +70,35 @@ private boolean shouldRetryBasedOnBigQueryRetryConfig(
6970
*/
7071
String errorDesc;
7172
if (previousThrowable != null && (errorDesc = previousThrowable.getMessage()) != null) {
73+
errorDesc = errorDesc.toLowerCase(); // for case insensitive comparison
7274
for (Iterator<String> retriableMessages =
7375
bigQueryRetryConfig.getRetriableErrorMessages().iterator();
7476
retriableMessages.hasNext(); ) {
75-
if (errorDesc.contains(retriableMessages.next())) { // Error message should be retried
77+
if (errorDesc.contains(
78+
retriableMessages
79+
.next()
80+
.toLowerCase())) { // Error message should be retried, implementing cases
81+
// insensitive match
82+
return true;
83+
}
84+
}
85+
// Check if there's a regex which matches the error message. This avoids too many regex
86+
// matches which is expensive
87+
for (Iterator<String> retriableRegExes = bigQueryRetryConfig.getRetriableRegExes().iterator();
88+
retriableRegExes.hasNext(); ) {
89+
if (matchRegEx(retriableRegExes.next(), errorDesc)) {
7690
return true;
7791
}
7892
}
7993
}
8094
return false;
8195
}
8296

97+
public static boolean matchRegEx(
98+
String retriableRegEx, String errorDesc) { // cases insensitive match regex matching
99+
return Pattern.matches(retriableRegEx.toLowerCase(), errorDesc.toLowerCase());
100+
}
101+
83102
/*Duplicating this method as it can not be inherited from the RetryAlgorithm due to the default access modifier*/
84103
boolean shouldRetryBasedOnResult(
85104
RetryingContext context, Throwable previousThrowable, ResponseT previousResponse) {

google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryRetryConfig.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,25 @@
2121

2222
public class BigQueryRetryConfig {
2323
private final ImmutableSet<String> retriableErrorMessages;
24+
private final ImmutableSet<String> retriableRegExes;
2425

2526
private BigQueryRetryConfig(Builder builder) {
2627
retriableErrorMessages = builder.retriableErrorMessages.build();
28+
retriableRegExes = builder.retriableRegExes.build();
2729
}
2830

2931
public ImmutableSet<String> getRetriableErrorMessages() {
3032
return retriableErrorMessages;
3133
}
3234

35+
public ImmutableSet<String> getRetriableRegExes() {
36+
return retriableRegExes;
37+
}
38+
3339
// BigQueryRetryConfig builder
3440
public static class Builder {
3541
private final ImmutableSet.Builder<String> retriableErrorMessages = ImmutableSet.builder();
42+
private final ImmutableSet.Builder<String> retriableRegExes = ImmutableSet.builder();
3643

3744
private Builder() {}
3845

@@ -43,6 +50,13 @@ public final Builder retryOnMessage(String... errorMessages) {
4350
return this;
4451
}
4552

53+
public final Builder retryOnRegEx(String... regExPatterns) {
54+
for (String regExPattern : regExPatterns) {
55+
retriableRegExes.add(checkNotNull(regExPattern));
56+
}
57+
return this;
58+
}
59+
4660
public BigQueryRetryConfig build() {
4761
return new BigQueryRetryConfig(this);
4862
}

google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -508,11 +508,12 @@ public class BigQueryImplTest {
508508
.setEtag(ETAG)
509509
.setVersion(1)
510510
.build();
511-
512511
private BigQueryOptions options;
513512
private BigQueryRpcFactory rpcFactoryMock;
514513
private BigQueryRpc bigqueryRpcMock;
515514
private BigQuery bigquery;
515+
private static final String RATE_LIMIT_ERROR_MSG =
516+
"Job exceeded rate limits: Your table exceeded quota for table update operations. For more information, see https://cloud.google.com/bigquery/docs/troubleshoot-quotas";
516517

517518
@Captor private ArgumentCaptor<Map<BigQueryRpc.Option, Object>> capturedOptions;
518519
@Captor private ArgumentCaptor<com.google.api.services.bigquery.model.Job> jobCapture;
@@ -2439,9 +2440,7 @@ public void testFastQueryRateLimitIdempotency() throws Exception {
24392440
.thenThrow(new BigQueryException(504, "Gateway Timeout"))
24402441
.thenThrow(
24412442
new BigQueryException(
2442-
400,
2443-
BigQueryErrorMessages
2444-
.RATE_LIMIT_EXCEEDED_MSG)) // retrial on based on RATE_LIMIT_EXCEEDED_MSG
2443+
400, RATE_LIMIT_ERROR_MSG)) // retrial on based on RATE_LIMIT_EXCEEDED_MSG
24452444
.thenReturn(responsePb);
24462445

24472446
bigquery =
@@ -2470,6 +2469,30 @@ public void testFastQueryRateLimitIdempotency() throws Exception {
24702469
verify(bigqueryRpcMock, times(6)).queryRpc(eq(PROJECT), requestPbCapture.capture());
24712470
}
24722471

2472+
@Test
2473+
public void testRateLimitRegEx() throws Exception {
2474+
String msg2 =
2475+
"Job eceeded rate limits: Your table exceeded quota for table update operations. For more information, see https://cloud.google.com/bigquery/docs/troubleshoot-quotas";
2476+
String msg3 = "exceeded rate exceeded quota for table update";
2477+
String msg4 = "exceeded rate limits";
2478+
assertTrue(
2479+
BigQueryRetryAlgorithm.matchRegEx(
2480+
BigQueryErrorMessages.RetryRegExPatterns.RATE_LIMIT_EXCEEDED_REGEX,
2481+
RATE_LIMIT_ERROR_MSG));
2482+
assertFalse(
2483+
BigQueryRetryAlgorithm.matchRegEx(
2484+
BigQueryErrorMessages.RetryRegExPatterns.RATE_LIMIT_EXCEEDED_REGEX,
2485+
msg2.toLowerCase()));
2486+
assertFalse(
2487+
BigQueryRetryAlgorithm.matchRegEx(
2488+
BigQueryErrorMessages.RetryRegExPatterns.RATE_LIMIT_EXCEEDED_REGEX,
2489+
msg3.toLowerCase()));
2490+
assertTrue(
2491+
BigQueryRetryAlgorithm.matchRegEx(
2492+
BigQueryErrorMessages.RetryRegExPatterns.RATE_LIMIT_EXCEEDED_REGEX,
2493+
msg4.toLowerCase()));
2494+
}
2495+
24732496
@Test
24742497
public void testFastQueryDDLShouldRetry() throws Exception {
24752498
com.google.api.services.bigquery.model.QueryResponse responsePb =

0 commit comments

Comments
 (0)