Skip to content

Commit 24f8cc0

Browse files
test: add integration test to ensure that reverse scan resumption produces correct results (#2029)
Change-Id: I909ec95bebe87219d66b387cd80e7095e75252a2 Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [ ] Make sure to open an issue as a [bug/issue](https://togithub.com/googleapis/java-bigtable/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea - [ ] Ensure the tests and linter pass - [ ] Code coverage does not decrease (if any source code was changed) - [ ] Appropriate docs were updated (if necessary) - [ ] Rollback plan is reviewed and LGTMed Fixes #<issue_number_goes_here> ☕️ If you write sample code, please follow the [samples format]( https://togithub.com/GoogleCloudPlatform/java-docs-samples/blob/main/SAMPLE_FORMAT.md).
1 parent 4b64482 commit 24f8cc0

File tree

2 files changed

+100
-1
lines changed

2 files changed

+100
-1
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.27.0')
53+
implementation platform('com.google.cloud:libraries-bom:26.28.0')
5454
5555
implementation 'com.google.cloud:google-cloud-bigtable'
5656
```

google-cloud-bigtable/src/test/java/com/google/cloud/bigtable/data/v2/it/ReadIT.java

+99
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@
1818
import static com.google.common.truth.Truth.assertThat;
1919
import static com.google.common.truth.TruthJUnit.assume;
2020

21+
import com.google.api.core.ApiFunction;
2122
import com.google.api.core.ApiFuture;
2223
import com.google.api.core.ApiFutureCallback;
2324
import com.google.api.core.ApiFutures;
2425
import com.google.api.core.SettableApiFuture;
26+
import com.google.api.gax.batching.Batcher;
27+
import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;
2528
import com.google.api.gax.rpc.ResponseObserver;
2629
import com.google.api.gax.rpc.StreamController;
2730
import com.google.cloud.bigtable.data.v2.BigtableDataClient;
31+
import com.google.cloud.bigtable.data.v2.BigtableDataSettings;
2832
import com.google.cloud.bigtable.data.v2.models.BulkMutation;
2933
import com.google.cloud.bigtable.data.v2.models.Query;
3034
import com.google.cloud.bigtable.data.v2.models.Range.ByteStringRange;
@@ -38,8 +42,17 @@
3842
import com.google.common.collect.Lists;
3943
import com.google.common.util.concurrent.MoreExecutors;
4044
import com.google.protobuf.ByteString;
45+
import io.grpc.CallOptions;
46+
import io.grpc.Channel;
47+
import io.grpc.ClientCall;
48+
import io.grpc.ClientInterceptor;
49+
import io.grpc.ManagedChannelBuilder;
50+
import io.grpc.MethodDescriptor;
51+
import java.io.IOException;
4152
import java.util.ArrayList;
53+
import java.util.Collections;
4254
import java.util.List;
55+
import java.util.Random;
4356
import java.util.UUID;
4457
import java.util.concurrent.CountDownLatch;
4558
import java.util.concurrent.ExecutionException;
@@ -308,6 +321,92 @@ public void reversed() {
308321
.inOrder();
309322
}
310323

324+
@Test
325+
public void reversedWithForcedResumption() throws IOException, InterruptedException {
326+
assume()
327+
.withMessage("reverse scans are not supported in the emulator")
328+
.that(testEnvRule.env())
329+
.isNotInstanceOf(EmulatorEnv.class);
330+
331+
BigtableDataClient client = testEnvRule.env().getDataClient();
332+
String tableId = testEnvRule.env().getTableId();
333+
String familyId = testEnvRule.env().getFamilyId();
334+
String uniqueKey = prefix + "-rev-queries2";
335+
336+
// Add enough rows that ensures resumption logic is forced
337+
Random random;
338+
List<Row> expectedResults;
339+
try (Batcher<RowMutationEntry, Void> batcher = client.newBulkMutationBatcher(tableId)) {
340+
341+
byte[] valueBytes = new byte[1024];
342+
random = new Random();
343+
344+
expectedResults = new ArrayList<>();
345+
346+
for (int i = 0; i < 2 * 1024; i++) {
347+
ByteString key = ByteString.copyFromUtf8(String.format("%s-%05d", uniqueKey, i));
348+
ByteString qualifier = ByteString.copyFromUtf8("q");
349+
long timestamp = System.currentTimeMillis() * 1000;
350+
random.nextBytes(valueBytes);
351+
ByteString value = ByteString.copyFrom(valueBytes);
352+
353+
batcher.add(RowMutationEntry.create(key).setCell(familyId, qualifier, timestamp, value));
354+
expectedResults.add(
355+
Row.create(
356+
key,
357+
ImmutableList.of(
358+
RowCell.create(familyId, qualifier, timestamp, ImmutableList.of(), value))));
359+
}
360+
}
361+
Collections.reverse(expectedResults);
362+
363+
BigtableDataSettings.Builder settingsBuilder =
364+
testEnvRule.env().getDataClientSettings().toBuilder();
365+
366+
settingsBuilder.stubSettings().readRowsSettings().retrySettings().setMaxAttempts(100);
367+
368+
InstantiatingGrpcChannelProvider.Builder transport =
369+
((InstantiatingGrpcChannelProvider)
370+
settingsBuilder.stubSettings().getTransportChannelProvider())
371+
.toBuilder();
372+
ApiFunction<ManagedChannelBuilder, ManagedChannelBuilder> oldConfigurator =
373+
transport.getChannelConfigurator();
374+
375+
// Randomly camp the deadline to force a timeout to force a retry
376+
transport.setChannelConfigurator(
377+
(ManagedChannelBuilder c) -> {
378+
if (oldConfigurator != null) {
379+
c = oldConfigurator.apply(c);
380+
}
381+
return c.intercept(
382+
new ClientInterceptor() {
383+
@Override
384+
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
385+
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
386+
if (method.getBareMethodName().equals("ReadRows")) {
387+
callOptions =
388+
callOptions.withDeadlineAfter(random.nextInt(200), TimeUnit.MILLISECONDS);
389+
}
390+
391+
return next.newCall(method, callOptions);
392+
}
393+
});
394+
});
395+
settingsBuilder.stubSettings().setTransportChannelProvider(transport.build());
396+
397+
try (BigtableDataClient patchedClient = BigtableDataClient.create(settingsBuilder.build())) {
398+
for (int i = 0; i < 10; i++) {
399+
List<Row> actualResults = new ArrayList<>();
400+
for (Row row :
401+
patchedClient.readRows(Query.create(tableId).prefix(uniqueKey).reversed(true))) {
402+
actualResults.add(row);
403+
Thread.sleep(1);
404+
}
405+
assertThat(actualResults).containsExactlyElementsIn(expectedResults).inOrder();
406+
}
407+
}
408+
}
409+
311410
@Test
312411
public void readSingleNonexistentAsyncCallback() throws Exception {
313412
ApiFuture<Row> future =

0 commit comments

Comments
 (0)