Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
2c543a1
test(showcase): add integration tests for exponential backoff retries…
nnicolee Jul 28, 2026
d27e2d0
Merge branch 'main' of github.com:googleapis/google-cloud-java into t…
nnicolee Jul 28, 2026
490cb36
test(showcase): simplify retry client configuration to use SequenceSe…
nnicolee Jul 28, 2026
eb5cf39
Merge branch 'main' into test/showcase-retries
nnicolee Jul 28, 2026
51960b2
test(showcase): use lower bounds only for retry delays to prevent fla…
nnicolee Jul 29, 2026
b93d939
Merge branch 'main' of github.com:googleapis/google-cloud-java into t…
nnicolee Jul 30, 2026
4977b9b
test(showcase): added more retries test
nnicolee Jul 30, 2026
0fd9590
test(showcase): removed fully qualified names
nnicolee Jul 30, 2026
e698ada
test(showcase): removed redundant deprecation annotations
nnicolee Jul 30, 2026
5077788
test(showcase): added comments
nnicolee Jul 30, 2026
c1adbc9
test(showcase): refactor ITRetries following comments and DAMP standards
nnicolee Jul 31, 2026
db69bd2
Merge branch 'main' into test/showcase-retries
nnicolee Jul 31, 2026
c0cbe21
fixed formatting
nnicolee Jul 31, 2026
77da0c5
add assertAttemptSequence helper and fixed delay asserts
nnicolee Aug 3, 2026
b3efec3
added comments
nnicolee Aug 3, 2026
f37aa6c
Merge branch 'main' into test/showcase-retries
nnicolee Aug 3, 2026
5eb3103
Merge branch 'main' into test/showcase-retries
nnicolee Aug 4, 2026
1bc7fe7
Merge branch 'main' into test/showcase-retries
nnicolee Aug 11, 2026
797d34d
Merge branch 'main' into test/showcase-retries
nnicolee Aug 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.showcase.v1beta1.it;

import static com.google.common.truth.Truth.assertThat;

import com.google.api.gax.retrying.RetrySettings;
import com.google.api.gax.rpc.StatusCode;
import com.google.common.collect.ImmutableSet;
import com.google.rpc.Status;
import com.google.showcase.v1beta1.AttemptSequenceRequest;
import com.google.showcase.v1beta1.CreateSequenceRequest;
import com.google.showcase.v1beta1.GetSequenceReportRequest;
import com.google.showcase.v1beta1.Sequence;
import com.google.showcase.v1beta1.SequenceReport;
import com.google.showcase.v1beta1.SequenceServiceClient;
import com.google.showcase.v1beta1.it.util.TestClientInitializer;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

class ITRetries {

private static final Sequence STANDARD_SEQUENCE =
Comment thread
nnicolee marked this conversation as resolved.
Outdated
Sequence.newBuilder()
.addResponses(
Sequence.Response.newBuilder()
.setStatus(
Status.newBuilder()
.setCode(com.google.rpc.Code.UNAVAILABLE.getNumber())
.build())
.build())
.addResponses(
Sequence.Response.newBuilder()
.setStatus(
Status.newBuilder()
.setCode(com.google.rpc.Code.UNAVAILABLE.getNumber())
.build())
.build())
.addResponses(
Sequence.Response.newBuilder()
.setStatus(
Status.newBuilder()
.setCode(com.google.rpc.Code.UNAVAILABLE.getNumber())
.build())
.build())
.addResponses(
Sequence.Response.newBuilder()
.setStatus(
Status.newBuilder().setCode(com.google.rpc.Code.OK.getNumber()).build())
.build())
.build();
Comment thread
nnicolee marked this conversation as resolved.
Outdated

@SuppressWarnings("deprecation")
Comment thread
nnicolee marked this conversation as resolved.
Outdated
private static final RetrySettings STANDARD_RETRY_SETTINGS =
RetrySettings.newBuilder()
.setInitialRetryDelayDuration(java.time.Duration.ofMillis(100L))
.setRetryDelayMultiplier(2.0)
.setMaxRetryDelayDuration(java.time.Duration.ofMillis(1000L))
.setInitialRpcTimeoutDuration(java.time.Duration.ofMillis(1000L))
.setRpcTimeoutMultiplier(1.0)
.setMaxRpcTimeoutDuration(java.time.Duration.ofMillis(1000L))
.setTotalTimeoutDuration(java.time.Duration.ofMillis(5000L))
.setMaxAttempts(4)
.setJittered(false)
.build();
Comment thread
nnicolee marked this conversation as resolved.
Outdated

private static SequenceServiceClient grpcClient;
private static SequenceServiceClient httpjsonClient;

@BeforeAll
static void createClients() throws Exception {
grpcClient = TestClientInitializer.createGrpcSequenceClient();
httpjsonClient = TestClientInitializer.createHttpJsonSequenceClient();
}
Comment thread
nnicolee marked this conversation as resolved.

@AfterAll
static void destroyClients() throws InterruptedException {
grpcClient.close();
httpjsonClient.close();

grpcClient.awaitTermination(TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS);
httpjsonClient.awaitTermination(
TestClientInitializer.AWAIT_TERMINATION_SECONDS, TimeUnit.SECONDS);
}
Comment thread
nnicolee marked this conversation as resolved.

@Test
@SuppressWarnings("deprecation")
void testGrpc_retryExponentialBackoff() throws Exception {
Comment thread
nnicolee marked this conversation as resolved.
// Create a custom client with these retry settings on attemptSequence
try (SequenceServiceClient retryClient =
TestClientInitializer.createGrpcSequenceClientWithRetrySettings(
STANDARD_RETRY_SETTINGS, ImmutableSet.of(StatusCode.Code.UNAVAILABLE))) {

Sequence createdSequence =
grpcClient.createSequence(
CreateSequenceRequest.newBuilder().setSequence(STANDARD_SEQUENCE).build());

// 3. Trigger the sequence attempts using the retrying client
retryClient.attemptSequence(
AttemptSequenceRequest.newBuilder().setName(createdSequence.getName()).build());

// 4. Retrieve the sequence report to inspect delay measurements
SequenceReport report =
grpcClient.getSequenceReport(
GetSequenceReportRequest.newBuilder()
.setName(createdSequence.getName() + "/sequenceReport")
.build());

// 5. Assert attempts count and delays
verifySequenceReport(report);
}
}

@Test
@SuppressWarnings("deprecation")
void testHttpJson_retryExponentialBackoff() throws Exception {
// Create a custom client with these retry settings on attemptSequence
try (SequenceServiceClient retryClient =
TestClientInitializer.createHttpJsonSequenceClientWithRetrySettings(
STANDARD_RETRY_SETTINGS, ImmutableSet.of(StatusCode.Code.UNAVAILABLE))) {

Sequence createdSequence =
httpjsonClient.createSequence(
CreateSequenceRequest.newBuilder().setSequence(STANDARD_SEQUENCE).build());

// 3. Trigger the sequence attempts using the retrying client
retryClient.attemptSequence(
AttemptSequenceRequest.newBuilder().setName(createdSequence.getName()).build());

// 4. Retrieve the sequence report to inspect delay measurements
SequenceReport report =
httpjsonClient.getSequenceReport(
GetSequenceReportRequest.newBuilder()
.setName(createdSequence.getName() + "/sequenceReport")
.build());

// 5. Assert attempts count and delays
verifySequenceReport(report);
}
}

private void verifySequenceReport(SequenceReport report) {
Comment thread
nnicolee marked this conversation as resolved.
Outdated
List<SequenceReport.Attempt> attempts = report.getAttemptsList();
assertThat(attempts).hasSize(4);

// Verify the status of each attempt
assertThat(attempts.get(0).getStatus().getCode())
.isEqualTo(com.google.rpc.Code.UNAVAILABLE.getNumber());
assertThat(attempts.get(1).getStatus().getCode())
.isEqualTo(com.google.rpc.Code.UNAVAILABLE.getNumber());
assertThat(attempts.get(2).getStatus().getCode())
.isEqualTo(com.google.rpc.Code.UNAVAILABLE.getNumber());
assertThat(attempts.get(3).getStatus().getCode()).isEqualTo(com.google.rpc.Code.OK.getNumber());

// Verify delay intervals are within expected tolerances:
// Attempt 1 -> 2: ~100ms delay. Range [80ms, 250ms]
long delay1 = getDelayMs(attempts.get(1));
assertThat(delay1).isAtLeast(80L);
assertThat(delay1).isLessThan(250L);

// Attempt 2 -> 3: ~200ms delay. Range [180ms, 400ms]
long delay2 = getDelayMs(attempts.get(2));
assertThat(delay2).isAtLeast(180L);
assertThat(delay2).isLessThan(400L);

// Attempt 3 -> 4: ~400ms delay. Range [380ms, 700ms]
long delay3 = getDelayMs(attempts.get(3));
assertThat(delay3).isAtLeast(380L);
assertThat(delay3).isLessThan(700L);
}

private long getDelayMs(SequenceReport.Attempt attempt) {
com.google.protobuf.Duration attemptDelay = attempt.getAttemptDelay();
return attemptDelay.getSeconds() * 1000 + attemptDelay.getNanos() / 1_000_000;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@
import com.google.showcase.v1beta1.EchoSettings;
import com.google.showcase.v1beta1.IdentityClient;
import com.google.showcase.v1beta1.IdentitySettings;
import com.google.showcase.v1beta1.SequenceServiceClient;
import com.google.showcase.v1beta1.SequenceServiceSettings;
import com.google.showcase.v1beta1.WaitRequest;
import com.google.showcase.v1beta1.stub.EchoStub;
import com.google.showcase.v1beta1.stub.EchoStubSettings;
import com.google.showcase.v1beta1.stub.SequenceServiceStubSettings;
import io.grpc.ClientInterceptor;
import io.grpc.ManagedChannelBuilder;
import java.io.IOException;
Expand Down Expand Up @@ -476,4 +479,74 @@ public String getServiceName() {
return "showcase";
}
}

public static SequenceServiceClient createGrpcSequenceClient() throws Exception {
SequenceServiceSettings settings =
SequenceServiceSettings.newBuilder()
.setCredentialsProvider(NoCredentialsProvider.create())
.setTransportChannelProvider(
SequenceServiceSettings.defaultGrpcTransportProviderBuilder()
.setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
.build())
.setEndpoint(DEFAULT_GRPC_ENDPOINT)
.build();
return SequenceServiceClient.create(settings);
}

public static SequenceServiceClient createHttpJsonSequenceClient() throws Exception {
SequenceServiceSettings settings =
SequenceServiceSettings.newHttpJsonBuilder()
.setCredentialsProvider(NoCredentialsProvider.create())
.setTransportChannelProvider(
SequenceServiceSettings.defaultHttpJsonTransportProviderBuilder()
.setHttpTransport(
new NetHttpTransport.Builder().doNotValidateCertificate().build())
.setEndpoint(DEFAULT_HTTPJSON_ENDPOINT)
.build())
.build();
return SequenceServiceClient.create(settings);
}

public static SequenceServiceClient createGrpcSequenceClientWithRetrySettings(
RetrySettings retrySettings, Set<StatusCode.Code> retryableCodes) throws Exception {
SequenceServiceStubSettings.Builder stubSettingsBuilder =
SequenceServiceStubSettings.newBuilder();
stubSettingsBuilder
.attemptSequenceSettings()
.setRetrySettings(retrySettings)
.setRetryableCodes(retryableCodes);
SequenceServiceSettings settings = SequenceServiceSettings.create(stubSettingsBuilder.build());
settings =
settings.toBuilder()
.setCredentialsProvider(NoCredentialsProvider.create())
.setTransportChannelProvider(
SequenceServiceSettings.defaultGrpcTransportProviderBuilder()
.setChannelConfigurator(ManagedChannelBuilder::usePlaintext)
.build())
.setEndpoint(DEFAULT_GRPC_ENDPOINT)
.build();
return SequenceServiceClient.create(settings);
}
Comment thread
nnicolee marked this conversation as resolved.

public static SequenceServiceClient createHttpJsonSequenceClientWithRetrySettings(
RetrySettings retrySettings, Set<StatusCode.Code> retryableCodes) throws Exception {
SequenceServiceStubSettings.Builder stubSettingsBuilder =
SequenceServiceStubSettings.newHttpJsonBuilder();
stubSettingsBuilder
.attemptSequenceSettings()
.setRetrySettings(retrySettings)
.setRetryableCodes(retryableCodes);
SequenceServiceSettings settings = SequenceServiceSettings.create(stubSettingsBuilder.build());
settings =
settings.toBuilder()
.setCredentialsProvider(NoCredentialsProvider.create())
.setTransportChannelProvider(
SequenceServiceSettings.defaultHttpJsonTransportProviderBuilder()
.setHttpTransport(
new NetHttpTransport.Builder().doNotValidateCertificate().build())
.setEndpoint(DEFAULT_HTTPJSON_ENDPOINT)
.build())
.build();
return SequenceServiceClient.create(settings);
}
Comment thread
nnicolee marked this conversation as resolved.
}
Loading