Skip to content

Commit ccc2764

Browse files
chore: add gax package private classes (#2027)
* chore: add gax package private classes * fix format * add callables * update retry algorithm to be in sync with gax * fix clirr * mark as internal * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 24f8cc0 commit ccc2764

File tree

8 files changed

+741
-23
lines changed

8 files changed

+741
-23
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ If you are using Maven without the BOM, add this to your dependencies:
5050
If you are using Gradle 5.x or later, add this to your dependencies:
5151

5252
```Groovy
53-
implementation platform('com.google.cloud:libraries-bom:26.28.0')
53+
implementation platform('com.google.cloud:libraries-bom:26.29.0')
5454
5555
implementation 'com.google.cloud:google-cloud-bigtable'
5656
```

google-cloud-bigtable/clirr-ignored-differences.xml

+6
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,10 @@
150150
<differenceType>8001</differenceType>
151151
<className>com/google/cloud/bigtable/data/v2/stub/metrics/BigtableTracerBatchedUnaryCallable</className>
152152
</difference>
153+
<!-- InternalApi was updated -->
154+
<difference>
155+
<differenceType>6001</differenceType>
156+
<className>com/google/cloud/bigtable/gaxx/retrying/ApiResultRetryAlgorithm</className>
157+
<field>*</field>
158+
</difference>
153159
</differences>

google-cloud-bigtable/src/main/java/com/google/cloud/bigtable/gaxx/retrying/ApiResultRetryAlgorithm.java

+24-22
Original file line numberDiff line numberDiff line change
@@ -16,36 +16,38 @@
1616
package com.google.cloud.bigtable.gaxx.retrying;
1717

1818
import com.google.api.core.InternalApi;
19-
import com.google.api.gax.retrying.ResultRetryAlgorithm;
20-
import com.google.api.gax.retrying.TimedAttemptSettings;
19+
import com.google.api.gax.retrying.BasicResultRetryAlgorithm;
20+
import com.google.api.gax.retrying.RetryingContext;
2121
import com.google.api.gax.rpc.ApiException;
22-
import com.google.api.gax.rpc.DeadlineExceededException;
23-
import org.threeten.bp.Duration;
2422

2523
/** For internal use, public for technical reasons. */
2624
@InternalApi
27-
public class ApiResultRetryAlgorithm<ResponseT> implements ResultRetryAlgorithm<ResponseT> {
28-
// Duration to sleep on if the error is DEADLINE_EXCEEDED.
29-
public static final Duration DEADLINE_SLEEP_DURATION = Duration.ofMillis(1);
25+
public class ApiResultRetryAlgorithm<ResponseT> extends BasicResultRetryAlgorithm<ResponseT> {
3026

27+
/** Returns true if previousThrowable is an {@link ApiException} that is retryable. */
3128
@Override
32-
public TimedAttemptSettings createNextAttempt(
33-
Throwable prevThrowable, ResponseT prevResponse, TimedAttemptSettings prevSettings) {
34-
if (prevThrowable != null && prevThrowable instanceof DeadlineExceededException) {
35-
return TimedAttemptSettings.newBuilder()
36-
.setGlobalSettings(prevSettings.getGlobalSettings())
37-
.setRetryDelay(prevSettings.getRetryDelay())
38-
.setRpcTimeout(prevSettings.getRpcTimeout())
39-
.setRandomizedRetryDelay(DEADLINE_SLEEP_DURATION)
40-
.setAttemptCount(prevSettings.getAttemptCount() + 1)
41-
.setFirstAttemptStartTimeNanos(prevSettings.getFirstAttemptStartTimeNanos())
42-
.build();
43-
}
44-
return null;
29+
public boolean shouldRetry(Throwable previousThrowable, ResponseT previousResponse) {
30+
return (previousThrowable instanceof ApiException)
31+
&& ((ApiException) previousThrowable).isRetryable();
4532
}
4633

34+
/**
35+
* If {@link RetryingContext#getRetryableCodes()} is not null: Returns true if the status code of
36+
* previousThrowable is in the list of retryable code of the {@link RetryingContext}.
37+
*
38+
* <p>Otherwise it returns the result of {@link #shouldRetry(Throwable, Object)}.
39+
*/
4740
@Override
48-
public boolean shouldRetry(Throwable prevThrowable, ResponseT prevResponse) {
49-
return (prevThrowable instanceof ApiException) && ((ApiException) prevThrowable).isRetryable();
41+
public boolean shouldRetry(
42+
RetryingContext context, Throwable previousThrowable, ResponseT previousResponse) {
43+
if (context.getRetryableCodes() != null) {
44+
// Ignore the isRetryable() value of the throwable if the RetryingContext has a specific list
45+
// of codes that should be retried.
46+
return (previousThrowable instanceof ApiException)
47+
&& context
48+
.getRetryableCodes()
49+
.contains(((ApiException) previousThrowable).getStatusCode().getCode());
50+
}
51+
return shouldRetry(previousThrowable, previousResponse);
5052
}
5153
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.bigtable.gaxx.retrying;
17+
18+
import com.google.api.core.ApiFuture;
19+
import com.google.api.core.ApiFutures;
20+
import com.google.api.core.InternalApi;
21+
import com.google.api.gax.retrying.NonCancellableFuture;
22+
import com.google.api.gax.retrying.RetryingFuture;
23+
import com.google.api.gax.rpc.ApiCallContext;
24+
import com.google.api.gax.rpc.UnaryCallable;
25+
import com.google.common.base.Preconditions;
26+
import java.util.concurrent.Callable;
27+
import org.threeten.bp.Duration;
28+
29+
// TODO: remove this once ApiResultRetryAlgorithm is added to gax.
30+
/**
31+
* A callable representing an attempt to make an RPC call. This class is used from {@link
32+
* RetryingCallable}.
33+
*
34+
* @param <RequestT> request type
35+
* @param <ResponseT> response type
36+
*/
37+
@InternalApi
38+
public class AttemptCallable<RequestT, ResponseT> implements Callable<ResponseT> {
39+
private final UnaryCallable<RequestT, ResponseT> callable;
40+
private final RequestT request;
41+
private final ApiCallContext originalCallContext;
42+
43+
private volatile RetryingFuture<ResponseT> externalFuture;
44+
45+
AttemptCallable(
46+
UnaryCallable<RequestT, ResponseT> callable, RequestT request, ApiCallContext callContext) {
47+
this.callable = Preconditions.checkNotNull(callable);
48+
this.request = Preconditions.checkNotNull(request);
49+
this.originalCallContext = Preconditions.checkNotNull(callContext);
50+
}
51+
52+
public void setExternalFuture(RetryingFuture<ResponseT> externalFuture) {
53+
this.externalFuture = Preconditions.checkNotNull(externalFuture);
54+
}
55+
56+
@Override
57+
public ResponseT call() {
58+
ApiCallContext callContext = originalCallContext;
59+
60+
try {
61+
// Set the RPC timeout if the caller did not provide their own.
62+
Duration rpcTimeout = externalFuture.getAttemptSettings().getRpcTimeout();
63+
if (!rpcTimeout.isZero() && callContext.getTimeout() == null) {
64+
callContext = callContext.withTimeout(rpcTimeout);
65+
}
66+
67+
externalFuture.setAttemptFuture(new NonCancellableFuture<ResponseT>());
68+
if (externalFuture.isDone()) {
69+
return null;
70+
}
71+
72+
callContext
73+
.getTracer()
74+
.attemptStarted(request, externalFuture.getAttemptSettings().getOverallAttemptCount());
75+
76+
ApiFuture<ResponseT> internalFuture = callable.futureCall(request, callContext);
77+
externalFuture.setAttemptFuture(internalFuture);
78+
} catch (Throwable e) {
79+
externalFuture.setAttemptFuture(ApiFutures.<ResponseT>immediateFailedFuture(e));
80+
}
81+
82+
return null;
83+
}
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.bigtable.gaxx.retrying;
17+
18+
import com.google.api.core.InternalApi;
19+
import com.google.api.gax.retrying.ExponentialRetryAlgorithm;
20+
import com.google.api.gax.retrying.RetryAlgorithm;
21+
import com.google.api.gax.retrying.RetrySettings;
22+
import com.google.api.gax.retrying.ScheduledRetryingExecutor;
23+
import com.google.api.gax.retrying.StreamingRetryAlgorithm;
24+
import com.google.api.gax.rpc.ClientContext;
25+
import com.google.api.gax.rpc.ServerStreamingCallSettings;
26+
import com.google.api.gax.rpc.ServerStreamingCallable;
27+
import com.google.api.gax.rpc.StatusCode;
28+
import com.google.api.gax.rpc.UnaryCallSettings;
29+
import com.google.api.gax.rpc.UnaryCallable;
30+
import java.util.Collection;
31+
32+
// TODO: remove this once ApiResultRetryAlgorithm is added to gax.
33+
/**
34+
* Class with utility methods to create callable objects using provided settings.
35+
*
36+
* <p>The callable objects wrap a given direct callable with features like retry and exception
37+
* translation.
38+
*/
39+
@InternalApi
40+
public class Callables {
41+
42+
private Callables() {}
43+
44+
public static <RequestT, ResponseT> UnaryCallable<RequestT, ResponseT> retrying(
45+
UnaryCallable<RequestT, ResponseT> innerCallable,
46+
UnaryCallSettings<?, ?> callSettings,
47+
ClientContext clientContext) {
48+
49+
UnaryCallSettings<?, ?> settings = callSettings;
50+
51+
if (areRetriesDisabled(settings.getRetryableCodes(), settings.getRetrySettings())) {
52+
// When retries are disabled, the total timeout can be treated as the rpc timeout.
53+
settings =
54+
settings
55+
.toBuilder()
56+
.setSimpleTimeoutNoRetries(settings.getRetrySettings().getTotalTimeout())
57+
.build();
58+
}
59+
60+
RetryAlgorithm<ResponseT> retryAlgorithm =
61+
new RetryAlgorithm<>(
62+
new ApiResultRetryAlgorithm<ResponseT>(),
63+
new ExponentialRetryAlgorithm(settings.getRetrySettings(), clientContext.getClock()));
64+
ScheduledRetryingExecutor<ResponseT> retryingExecutor =
65+
new ScheduledRetryingExecutor<>(retryAlgorithm, clientContext.getExecutor());
66+
return new RetryingCallable<>(
67+
clientContext.getDefaultCallContext(), innerCallable, retryingExecutor);
68+
}
69+
70+
public static <RequestT, ResponseT> ServerStreamingCallable<RequestT, ResponseT> retrying(
71+
ServerStreamingCallable<RequestT, ResponseT> innerCallable,
72+
ServerStreamingCallSettings<RequestT, ResponseT> callSettings,
73+
ClientContext clientContext) {
74+
75+
ServerStreamingCallSettings<RequestT, ResponseT> settings = callSettings;
76+
if (areRetriesDisabled(settings.getRetryableCodes(), settings.getRetrySettings())) {
77+
// When retries are disabled, the total timeout can be treated as the rpc timeout.
78+
settings =
79+
settings
80+
.toBuilder()
81+
.setSimpleTimeoutNoRetries(settings.getRetrySettings().getTotalTimeout())
82+
.build();
83+
}
84+
85+
StreamingRetryAlgorithm<Void> retryAlgorithm =
86+
new StreamingRetryAlgorithm<>(
87+
new ApiResultRetryAlgorithm<Void>(),
88+
new ExponentialRetryAlgorithm(settings.getRetrySettings(), clientContext.getClock()));
89+
90+
ScheduledRetryingExecutor<Void> retryingExecutor =
91+
new ScheduledRetryingExecutor<>(retryAlgorithm, clientContext.getExecutor());
92+
93+
return new RetryingServerStreamingCallable<>(
94+
innerCallable, retryingExecutor, settings.getResumptionStrategy());
95+
}
96+
97+
private static boolean areRetriesDisabled(
98+
Collection<StatusCode.Code> retryableCodes, RetrySettings retrySettings) {
99+
return retrySettings.getMaxAttempts() == 1
100+
|| retryableCodes.isEmpty()
101+
|| (retrySettings.getMaxAttempts() == 0 && retrySettings.getTotalTimeout().isZero());
102+
}
103+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.bigtable.gaxx.retrying;
17+
18+
import com.google.api.core.InternalApi;
19+
import com.google.api.gax.retrying.RetryingExecutorWithContext;
20+
import com.google.api.gax.retrying.RetryingFuture;
21+
import com.google.api.gax.rpc.ApiCallContext;
22+
import com.google.api.gax.rpc.UnaryCallable;
23+
import com.google.common.base.Preconditions;
24+
25+
// TODO: remove this once ApiResultRetryAlgorithm is added to gax.
26+
/**
27+
* A UnaryCallable that will keep issuing calls to an inner callable until it succeeds or times out.
28+
*/
29+
@InternalApi
30+
public class RetryingCallable<RequestT, ResponseT> extends UnaryCallable<RequestT, ResponseT> {
31+
private final ApiCallContext callContextPrototype;
32+
private final UnaryCallable<RequestT, ResponseT> callable;
33+
private final RetryingExecutorWithContext<ResponseT> executor;
34+
35+
public RetryingCallable(
36+
ApiCallContext callContextPrototype,
37+
UnaryCallable<RequestT, ResponseT> callable,
38+
RetryingExecutorWithContext<ResponseT> executor) {
39+
this.callContextPrototype = (ApiCallContext) Preconditions.checkNotNull(callContextPrototype);
40+
this.callable = (UnaryCallable) Preconditions.checkNotNull(callable);
41+
this.executor = (RetryingExecutorWithContext) Preconditions.checkNotNull(executor);
42+
}
43+
44+
public RetryingFuture<ResponseT> futureCall(RequestT request, ApiCallContext inputContext) {
45+
ApiCallContext context = this.callContextPrototype.nullToSelf(inputContext);
46+
AttemptCallable<RequestT, ResponseT> retryCallable =
47+
new AttemptCallable(this.callable, request, context);
48+
RetryingFuture<ResponseT> retryingFuture =
49+
this.executor.createFuture(retryCallable, inputContext);
50+
retryCallable.setExternalFuture(retryingFuture);
51+
retryCallable.call();
52+
return retryingFuture;
53+
}
54+
55+
public String toString() {
56+
return String.format("retrying(%s)", this.callable);
57+
}
58+
}

0 commit comments

Comments
 (0)