Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ If you are using Maven without the BOM, add this to your dependencies:
If you are using Gradle 5.x or later, add this to your dependencies:

```Groovy
implementation platform('com.google.cloud:libraries-bom:26.36.0')
implementation platform('com.google.cloud:libraries-bom:26.37.0')

implementation 'com.google.cloud:google-cloud-bigtable'
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,9 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;

import com.google.api.gax.core.NoCredentialsProvider;
import com.google.api.gax.grpc.GrpcStatusCode;
import com.google.api.gax.grpc.GrpcTransportChannel;
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.ErrorDetails;
import com.google.api.gax.rpc.FixedTransportChannelProvider;
import com.google.api.gax.rpc.InternalException;
import com.google.api.gax.rpc.UnavailableException;
import com.google.bigtable.v2.BigtableGrpc;
Expand All @@ -45,6 +42,7 @@
import com.google.bigtable.v2.SampleRowKeysResponse;
import com.google.cloud.bigtable.data.v2.BigtableDataClient;
import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
import com.google.cloud.bigtable.data.v2.FakeServiceBuilder;
import com.google.cloud.bigtable.data.v2.models.BulkMutation;
import com.google.cloud.bigtable.data.v2.models.ConditionalRowMutation;
import com.google.cloud.bigtable.data.v2.models.Filters;
Expand All @@ -55,35 +53,45 @@
import com.google.cloud.bigtable.data.v2.models.ReadModifyWriteRow;
import com.google.cloud.bigtable.data.v2.models.RowMutation;
import com.google.cloud.bigtable.data.v2.models.RowMutationEntry;
import com.google.cloud.bigtable.data.v2.models.TableId;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Queues;
import com.google.protobuf.Any;
import com.google.rpc.RetryInfo;
import io.grpc.ForwardingServerCall;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.Server;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.grpc.stub.StreamObserver;
import io.grpc.testing.GrpcServerRule;
import java.io.IOException;
import java.time.Duration;
import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public class RetryInfoTest {

@Rule public GrpcServerRule serverRule = new GrpcServerRule();

private static final Metadata.Key<byte[]> ERROR_DETAILS_KEY =
Metadata.Key.of("grpc-status-details-bin", Metadata.BINARY_BYTE_MARSHALLER);

private final Set<String> methods = new HashSet<>();

private FakeBigtableService service;
private Server server;
private BigtableDataClient client;
private BigtableDataSettings.Builder settings;

Expand All @@ -94,29 +102,111 @@ public class RetryInfoTest {
@Before
public void setUp() throws IOException {
service = new FakeBigtableService();
serverRule.getServiceRegistry().addService(service);

ServerInterceptor serverInterceptor =
new ServerInterceptor() {
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> serverCall,
Metadata metadata,
ServerCallHandler<ReqT, RespT> serverCallHandler) {
return serverCallHandler.startCall(
new ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT>(serverCall) {
@Override
public void close(Status status, Metadata trailers) {
if (trailers.containsKey(ERROR_DETAILS_KEY)) {
methods.add(serverCall.getMethodDescriptor().getBareMethodName());
}
super.close(status, trailers);
}
},
metadata);
}
};
server = FakeServiceBuilder.create(service).intercept(serverInterceptor).start();

settings =
BigtableDataSettings.newBuilder()
BigtableDataSettings.newBuilderForEmulator(server.getPort())
.setProjectId("fake-project")
.setInstanceId("fake-instance")
.setCredentialsProvider(NoCredentialsProvider.create());

settings
.stubSettings()
.setTransportChannelProvider(
FixedTransportChannelProvider.create(
GrpcTransportChannel.create(serverRule.getChannel())))
// channel priming doesn't work with FixedTransportChannelProvider. Disable it for the test
.setRefreshingChannel(false)
.build();
.setInstanceId("fake-instance");

this.client = BigtableDataClient.create(settings.build());
}

@After
public void tearDown() {
if (client != null) {
client.close();
}
if (server != null) {
server.shutdown();
}
}

@Test
public void testReadRow() {
verifyRetryInfoIsUsed(() -> client.readRow("table", "row"), true);
public void testAllMethods() {
// Verify retry info is handled correctly for all the methods in data API.
verifyRetryInfoIsUsed(() -> client.readRow(TableId.of("table"), "row"), true);

attemptCounter.set(0);
verifyRetryInfoIsUsed(
() -> client.readRows(Query.create(TableId.of("table"))).iterator().hasNext(), true);

attemptCounter.set(0);
verifyRetryInfoIsUsed(
() ->
client.bulkMutateRows(
BulkMutation.create(TableId.of("fake-table"))
.add(RowMutationEntry.create("row-key-1").setCell("cf", "q", "v"))),
true);

attemptCounter.set(0);
verifyRetryInfoIsUsed(
() ->
client.mutateRow(
RowMutation.create(TableId.of("fake-table"), "key").setCell("cf", "q", "v")),
true);

attemptCounter.set(0);
verifyRetryInfoIsUsed(() -> client.sampleRowKeys(TableId.of("table")), true);

attemptCounter.set(0);
verifyRetryInfoIsUsed(
() ->
client.checkAndMutateRow(
ConditionalRowMutation.create("table", "key")
.condition(Filters.FILTERS.value().regex("old-value"))
.then(Mutation.create().setCell("cf", "q", "v"))),
true);

attemptCounter.set(0);
verifyRetryInfoIsUsed(
() ->
client.readModifyWriteRow(
ReadModifyWriteRow.create("table", "row").append("cf", "q", "v")),
true);

attemptCounter.set(0);
verifyRetryInfoIsUsed(
() -> client.readChangeStream(ReadChangeStreamQuery.create("table")).iterator().hasNext(),
true);

attemptCounter.set(0);
verifyRetryInfoIsUsed(
() -> client.generateInitialChangeStreamPartitions("table").iterator().hasNext(), true);

// Verify that the new data API methods are tested or excluded. This is enforced by
// introspecting grpc
// method descriptors.
Set<String> expected =
BigtableGrpc.getServiceDescriptor().getMethods().stream()
.map(MethodDescriptor::getBareMethodName)
.collect(Collectors.toSet());

// Exclude methods that don't support retry info
methods.add("PingAndWarm");

assertThat(methods).containsExactlyElementsIn(expected);
}

@Test
Expand Down Expand Up @@ -147,11 +237,6 @@ public void testReadRowServerNotReturningRetryInfoClientDisabledHandling() throw
}
}

@Test
public void testReadRows() {
verifyRetryInfoIsUsed(() -> client.readRows(Query.create("table")).iterator().hasNext(), true);
}

@Test
public void testReadRowsNonRetraybleErrorWithRetryInfo() {
verifyRetryInfoIsUsed(() -> client.readRows(Query.create("table")).iterator().hasNext(), false);
Expand Down Expand Up @@ -181,16 +266,6 @@ public void testReadRowsServerNotReturningRetryInfoClientDisabledHandling() thro
}
}

@Test
public void testMutateRows() {
verifyRetryInfoIsUsed(
() ->
client.bulkMutateRows(
BulkMutation.create("fake-table")
.add(RowMutationEntry.create("row-key-1").setCell("cf", "q", "v"))),
true);
}

@Test
public void testMutateRowsNonRetryableErrorWithRetryInfo() {
verifyRetryInfoIsUsed(
Expand Down Expand Up @@ -238,12 +313,6 @@ public void testMutateRowsServerNotReturningRetryInfoClientDisabledHandling() th
}
}

@Test
public void testMutateRow() {
verifyRetryInfoIsUsed(
() -> client.mutateRow(RowMutation.create("table", "key").setCell("cf", "q", "v")), true);
}

@Test
public void testMutateRowNonRetryableErrorWithRetryInfo() {
verifyRetryInfoIsUsed(
Expand Down Expand Up @@ -278,11 +347,6 @@ public void testMutateRowServerNotReturningRetryInfoClientDisabledHandling() thr
}
}

@Test
public void testSampleRowKeys() {
verifyRetryInfoIsUsed(() -> client.sampleRowKeys("table"), true);
}

@Test
public void testSampleRowKeysNonRetryableErrorWithRetryInfo() {
verifyRetryInfoIsUsed(() -> client.sampleRowKeys("table"), false);
Expand Down Expand Up @@ -312,17 +376,6 @@ public void testSampleRowKeysServerNotReturningRetryInfoClientDisabledHandling()
}
}

@Test
public void testCheckAndMutateRow() {
verifyRetryInfoIsUsed(
() ->
client.checkAndMutateRow(
ConditionalRowMutation.create("table", "key")
.condition(Filters.FILTERS.value().regex("old-value"))
.then(Mutation.create().setCell("cf", "q", "v"))),
true);
}

@Test
public void testCheckAndMutateDisableRetryInfo() throws IOException {
settings.stubSettings().setEnableRetryInfo(false);
Expand Down Expand Up @@ -368,15 +421,6 @@ public void testCheckAndMutateServerNotReturningRetryInfoClientDisabledHandling(
}
}

@Test
public void testReadModifyWrite() {
verifyRetryInfoIsUsed(
() ->
client.readModifyWriteRow(
ReadModifyWriteRow.create("table", "row").append("cf", "q", "v")),
true);
}

@Test
public void testReadModifyWriteDisableRetryInfo() throws IOException {
settings.stubSettings().setEnableRetryInfo(false);
Expand Down Expand Up @@ -414,13 +458,6 @@ public void testReadModifyWriteNotReturningRetryInfoClientDisabledHandling() thr
}
}

@Test
public void testReadChangeStream() {
verifyRetryInfoIsUsed(
() -> client.readChangeStream(ReadChangeStreamQuery.create("table")).iterator().hasNext(),
true);
}

@Test
public void testReadChangeStreamNonRetryableErrorWithRetryInfo() {
verifyRetryInfoIsUsed(
Expand Down Expand Up @@ -465,12 +502,6 @@ public void testReadChangeStreamNotReturningRetryInfoClientDisabledHandling() th
}
}

@Test
public void testGenerateInitialChangeStreamPartition() {
verifyRetryInfoIsUsed(
() -> client.generateInitialChangeStreamPartitions("table").iterator().hasNext(), true);
}

@Test
public void testGenerateInitialChangeStreamPartitionNonRetryableError() {
verifyRetryInfoIsUsed(
Expand Down