1616package com .google .cloud .bigtable .data .v2 .stub ;
1717
1818import static com .google .common .truth .Truth .assertThat ;
19+ import static org .junit .Assert .assertThrows ;
1920
2021import com .google .api .gax .core .NoCredentialsProvider ;
2122import com .google .api .gax .grpc .GrpcStatusCode ;
@@ -132,6 +133,20 @@ public void testReadRowDisableRetryInfo() throws IOException {
132133 }
133134 }
134135
136+ @ Test
137+ public void testReadRowServerNotReturningRetryInfo () {
138+ verifyNoRetryInfo (() -> client .readRow ("table" , "row" ), true );
139+ }
140+
141+ @ Test
142+ public void testReadRowServerNotReturningRetryInfoClientDisabledHandling () throws IOException {
143+ settings .stubSettings ().setEnableRetryInfo (false );
144+
145+ try (BigtableDataClient newClient = BigtableDataClient .create (settings .build ())) {
146+ verifyNoRetryInfo (() -> newClient .readRow ("table" , "row" ), true );
147+ }
148+ }
149+
135150 @ Test
136151 public void testReadRows () {
137152 verifyRetryInfoIsUsed (() -> client .readRows (Query .create ("table" )).iterator ().hasNext (), true );
@@ -152,6 +167,20 @@ public void testReadRowsDisableRetryInfo() throws IOException {
152167 }
153168 }
154169
170+ @ Test
171+ public void testReadRowsServerNotReturningRetryInfo () {
172+ verifyNoRetryInfo (() -> client .readRows (Query .create ("table" )).iterator ().hasNext (), true );
173+ }
174+
175+ @ Test
176+ public void testReadRowsServerNotReturningRetryInfoClientDisabledHandling () throws IOException {
177+ settings .stubSettings ().setEnableRetryInfo (false );
178+
179+ try (BigtableDataClient newClient = BigtableDataClient .create (settings .build ())) {
180+ verifyNoRetryInfo (() -> newClient .readRows (Query .create ("table" )).iterator ().hasNext (), true );
181+ }
182+ }
183+
155184 @ Test
156185 public void testMutateRows () {
157186 verifyRetryInfoIsUsed (
@@ -185,6 +214,30 @@ public void testMutateRowsDisableRetryInfo() throws IOException {
185214 }
186215 }
187216
217+ @ Test
218+ public void testMutateRowsServerNotReturningRetryInfo () {
219+ verifyNoRetryInfo (
220+ () ->
221+ client .bulkMutateRows (
222+ BulkMutation .create ("fake-table" )
223+ .add (RowMutationEntry .create ("row-key-1" ).setCell ("cf" , "q" , "v" ))),
224+ true );
225+ }
226+
227+ @ Test
228+ public void testMutateRowsServerNotReturningRetryInfoClientDisabledHandling () throws IOException {
229+ settings .stubSettings ().setEnableRetryInfo (false );
230+
231+ try (BigtableDataClient newClient = BigtableDataClient .create (settings .build ())) {
232+ verifyNoRetryInfo (
233+ () ->
234+ newClient .bulkMutateRows (
235+ BulkMutation .create ("fake-table" )
236+ .add (RowMutationEntry .create ("row-key-1" ).setCell ("cf" , "q" , "v" ))),
237+ true );
238+ }
239+ }
240+
188241 @ Test
189242 public void testMutateRow () {
190243 verifyRetryInfoIsUsed (
@@ -208,6 +261,23 @@ public void testMutateRowDisableRetryInfo() throws IOException {
208261 }
209262 }
210263
264+ @ Test
265+ public void testMutateRowServerNotReturningRetryInfo () {
266+ verifyNoRetryInfo (
267+ () -> client .mutateRow (RowMutation .create ("table" , "key" ).setCell ("cf" , "q" , "v" )), true );
268+ }
269+
270+ @ Test
271+ public void testMutateRowServerNotReturningRetryInfoClientDisabledHandling () throws IOException {
272+ settings .stubSettings ().setEnableRetryInfo (false );
273+
274+ try (BigtableDataClient newClient = BigtableDataClient .create (settings .build ())) {
275+ verifyNoRetryInfo (
276+ () -> newClient .mutateRow (RowMutation .create ("table" , "key" ).setCell ("cf" , "q" , "v" )),
277+ true );
278+ }
279+ }
280+
211281 @ Test
212282 public void testSampleRowKeys () {
213283 verifyRetryInfoIsUsed (() -> client .sampleRowKeys ("table" ), true );
@@ -227,6 +297,21 @@ public void testSampleRowKeysDisableRetryInfo() throws IOException {
227297 }
228298 }
229299
300+ @ Test
301+ public void testSampleRowKeysServerNotReturningRetryInfo () {
302+ verifyNoRetryInfo (() -> client .sampleRowKeys ("table" ), true );
303+ }
304+
305+ @ Test
306+ public void testSampleRowKeysServerNotReturningRetryInfoClientDisabledHandling ()
307+ throws IOException {
308+ settings .stubSettings ().setEnableRetryInfo (false );
309+
310+ try (BigtableDataClient newClient = BigtableDataClient .create (settings .build ())) {
311+ verifyNoRetryInfo (() -> newClient .sampleRowKeys ("table" ), true );
312+ }
313+ }
314+
230315 @ Test
231316 public void testCheckAndMutateRow () {
232317 verifyRetryInfoIsUsed (
@@ -256,6 +341,33 @@ public void testCheckAndMutateDisableRetryInfo() throws IOException {
256341 }
257342 }
258343
344+ @ Test
345+ public void testCheckAndMutateServerNotReturningRetryInfo () {
346+ verifyNoRetryInfo (
347+ () ->
348+ client .checkAndMutateRow (
349+ ConditionalRowMutation .create ("table" , "key" )
350+ .condition (Filters .FILTERS .value ().regex ("old-value" ))
351+ .then (Mutation .create ().setCell ("cf" , "q" , "v" ))),
352+ false );
353+ }
354+
355+ @ Test
356+ public void testCheckAndMutateServerNotReturningRetryInfoClientDisabledHandling ()
357+ throws IOException {
358+ settings .stubSettings ().setEnableRetryInfo (false );
359+
360+ try (BigtableDataClient newClient = BigtableDataClient .create (settings .build ())) {
361+ verifyNoRetryInfo (
362+ () ->
363+ newClient .checkAndMutateRow (
364+ ConditionalRowMutation .create ("table" , "key" )
365+ .condition (Filters .FILTERS .value ().regex ("old-value" ))
366+ .then (Mutation .create ().setCell ("cf" , "q" , "v" ))),
367+ false );
368+ }
369+ }
370+
259371 @ Test
260372 public void testReadModifyWrite () {
261373 verifyRetryInfoIsUsed (
@@ -280,6 +392,28 @@ public void testReadModifyWriteDisableRetryInfo() throws IOException {
280392 }
281393 }
282394
395+ @ Test
396+ public void testReadModifyWriteServerNotReturningRetryInfo () {
397+ verifyNoRetryInfo (
398+ () ->
399+ client .readModifyWriteRow (
400+ ReadModifyWriteRow .create ("table" , "row" ).append ("cf" , "q" , "v" )),
401+ false );
402+ }
403+
404+ @ Test
405+ public void testReadModifyWriteNotReturningRetryInfoClientDisabledHandling () throws IOException {
406+ settings .stubSettings ().setEnableRetryInfo (false );
407+
408+ try (BigtableDataClient newClient = BigtableDataClient .create (settings .build ())) {
409+ verifyNoRetryInfo (
410+ () ->
411+ newClient .readModifyWriteRow (
412+ ReadModifyWriteRow .create ("table" , "row" ).append ("cf" , "q" , "v" )),
413+ false );
414+ }
415+ }
416+
283417 @ Test
284418 public void testReadChangeStream () {
285419 verifyRetryInfoIsUsed (
@@ -308,6 +442,28 @@ public void testReadChangeStreamDisableRetryInfo() throws IOException {
308442 }
309443 }
310444
445+ @ Test
446+ public void testReadChangeStreamServerNotReturningRetryInfo () {
447+ verifyNoRetryInfo (
448+ () -> client .readChangeStream (ReadChangeStreamQuery .create ("table" )).iterator ().hasNext (),
449+ true );
450+ }
451+
452+ @ Test
453+ public void testReadChangeStreamNotReturningRetryInfoClientDisabledHandling () throws IOException {
454+ settings .stubSettings ().setEnableRetryInfo (false );
455+
456+ try (BigtableDataClient newClient = BigtableDataClient .create (settings .build ())) {
457+ verifyNoRetryInfo (
458+ () ->
459+ newClient
460+ .readChangeStream (ReadChangeStreamQuery .create ("table" ))
461+ .iterator ()
462+ .hasNext (),
463+ true );
464+ }
465+ }
466+
311467 @ Test
312468 public void testGenerateInitialChangeStreamPartition () {
313469 verifyRetryInfoIsUsed (
@@ -330,6 +486,25 @@ public void testGenerateInitialChangeStreamPartitionDisableRetryInfo() throws IO
330486 }
331487 }
332488
489+ @ Test
490+ public void testGenerateInitialChangeStreamServerNotReturningRetryInfo () {
491+ verifyNoRetryInfo (
492+ () -> client .generateInitialChangeStreamPartitions ("table" ).iterator ().hasNext (), true );
493+ }
494+
495+ @ Test
496+ public void testGenerateInitialChangeStreamServerNotReturningRetryInfoClientDisabledHandling ()
497+ throws IOException {
498+ settings .stubSettings ().setEnableRetryInfo (false );
499+
500+ try (BigtableDataClient newClient = BigtableDataClient .create (settings .build ())) {
501+ verifyNoRetryInfo (
502+ () -> newClient .generateInitialChangeStreamPartitions ("table" ).iterator ().hasNext (),
503+ true );
504+ }
505+ }
506+
507+ // Test the case where server returns retry info and client enables handling of retry info
333508 private void verifyRetryInfoIsUsed (Runnable runnable , boolean retryableError ) {
334509 if (retryableError ) {
335510 enqueueRetryableExceptionWithDelay (delay );
@@ -344,6 +519,7 @@ private void verifyRetryInfoIsUsed(Runnable runnable, boolean retryableError) {
344519 assertThat (stopwatch .elapsed ()).isAtLeast (Duration .ofSeconds (delay .getSeconds ()));
345520 }
346521
522+ // Test the case where server returns retry info but client disabled handling of retry info
347523 private void verifyRetryInfoCanBeDisabled (Runnable runnable ) {
348524 enqueueRetryableExceptionWithDelay (delay );
349525 Stopwatch stopwatch = Stopwatch .createStarted ();
@@ -354,17 +530,58 @@ private void verifyRetryInfoCanBeDisabled(Runnable runnable) {
354530 assertThat (stopwatch .elapsed ()).isLessThan (Duration .ofSeconds (delay .getSeconds ()));
355531
356532 attemptCounter .set (0 );
357- ApiException exception = enqueueNonRetryableExceptionWithDelay (delay );
358- try {
533+ ApiException expectedApiException = enqueueNonRetryableExceptionWithDelay (delay );
534+ ApiException actualException =
535+ assertThrows ("non retryable operations should fail" , ApiException .class , runnable ::run );
536+ if (actualException instanceof MutateRowsException ) {
537+ assertThat (
538+ ((MutateRowsException ) actualException )
539+ .getFailedMutations ()
540+ .get (0 )
541+ .getError ()
542+ .getStatusCode ())
543+ .isEqualTo (expectedApiException .getStatusCode ());
544+ } else {
545+ assertThat (actualException .getStatusCode ()).isEqualTo (expectedApiException .getStatusCode ());
546+ }
547+ assertThat (attemptCounter .get ()).isEqualTo (1 );
548+ }
549+
550+ // Test the case where server does not return retry info
551+ private void verifyNoRetryInfo (Runnable runnable , boolean operationRetryable ) {
552+ enqueueRetryableExceptionNoRetryInfo ();
553+
554+ if (!operationRetryable ) {
555+ assertThrows ("non retryable operation should fail" , ApiException .class , runnable ::run );
556+ assertThat (attemptCounter .get ()).isEqualTo (1 );
557+ } else {
558+ Stopwatch stopwatch = Stopwatch .createStarted ();
359559 runnable .run ();
360- } catch (ApiException e ) {
361- if (e instanceof MutateRowsException ) {
362- assertThat (((MutateRowsException ) e ).getFailedMutations ().get (0 ).getError ().getStatusCode ())
363- .isEqualTo (exception .getStatusCode ());
364- } else {
365- assertThat (e .getStatusCode ()).isEqualTo (exception .getStatusCode ());
366- }
560+ stopwatch .stop ();
561+
562+ assertThat (attemptCounter .get ()).isEqualTo (2 );
563+ assertThat (stopwatch .elapsed ()).isLessThan (Duration .ofSeconds (delay .getSeconds ()));
367564 }
565+
566+ attemptCounter .set (0 );
567+
568+ ApiException expectedApiException = enqueueNonRetryableExceptionNoRetryInfo ();
569+
570+ ApiException actualApiException =
571+ assertThrows ("non retryable error should fail" , ApiException .class , runnable ::run );
572+ if (actualApiException instanceof MutateRowsException ) {
573+ assertThat (
574+ ((MutateRowsException ) actualApiException )
575+ .getFailedMutations ()
576+ .get (0 )
577+ .getError ()
578+ .getStatusCode ())
579+ .isEqualTo (expectedApiException .getStatusCode ());
580+ } else {
581+ assertThat (actualApiException .getStatusCode ())
582+ .isEqualTo (expectedApiException .getStatusCode ());
583+ }
584+
368585 assertThat (attemptCounter .get ()).isEqualTo (1 );
369586 }
370587
@@ -408,6 +625,27 @@ private ApiException enqueueNonRetryableExceptionWithDelay(com.google.protobuf.D
408625 return exception ;
409626 }
410627
628+ private void enqueueRetryableExceptionNoRetryInfo () {
629+ ApiException exception =
630+ new UnavailableException (
631+ new StatusRuntimeException (Status .UNAVAILABLE ),
632+ GrpcStatusCode .of (Status .Code .UNAVAILABLE ),
633+ true );
634+ service .expectations .add (exception );
635+ }
636+
637+ private ApiException enqueueNonRetryableExceptionNoRetryInfo () {
638+ ApiException exception =
639+ new InternalException (
640+ new StatusRuntimeException (Status .INTERNAL ),
641+ GrpcStatusCode .of (Status .Code .INTERNAL ),
642+ false );
643+
644+ service .expectations .add (exception );
645+
646+ return exception ;
647+ }
648+
411649 private class FakeBigtableService extends BigtableGrpc .BigtableImplBase {
412650 Queue <Exception > expectations = Queues .newArrayDeque ();
413651
0 commit comments