@@ -248,6 +248,156 @@ public void singleUseReadCooldownSkipsReplicaOnNextRequestForBypassTraffic() thr
248248 }
249249 }
250250
251+ @ Test
252+ public void singleUseReadReroutesOnUnavailableForBypassTraffic () throws Exception {
253+ try (SharedBackendReplicaHarness harness = SharedBackendReplicaHarness .create (2 );
254+ Spanner spanner = createSpanner (harness )) {
255+ configureBackend (harness , singleRowReadResultSet ("b" ));
256+ DatabaseClient client = spanner .getDatabaseClient (DatabaseId .of (PROJECT , INSTANCE , DATABASE ));
257+
258+ seedLocationMetadata (client );
259+ waitForReplicaRoutedRead (client , harness , 0 );
260+ harness .clearRequests ();
261+
262+ harness
263+ .replicas
264+ .get (0 )
265+ .putMethodErrors (
266+ SharedBackendReplicaHarness .METHOD_STREAMING_READ , unavailable ("isolated-replica" ));
267+
268+ try (ResultSet resultSet =
269+ client
270+ .singleUse ()
271+ .read (
272+ TABLE ,
273+ KeySet .singleKey (Key .of ("b" )),
274+ Arrays .asList ("k" ),
275+ Options .directedRead (DIRECTED_READ_OPTIONS ))) {
276+ assertTrue (resultSet .next ());
277+ }
278+
279+ assertEquals (
280+ 1 ,
281+ harness
282+ .replicas
283+ .get (0 )
284+ .getRequests (SharedBackendReplicaHarness .METHOD_STREAMING_READ )
285+ .size ());
286+ assertEquals (
287+ 1 ,
288+ harness
289+ .replicas
290+ .get (1 )
291+ .getRequests (SharedBackendReplicaHarness .METHOD_STREAMING_READ )
292+ .size ());
293+ assertEquals (
294+ 0 ,
295+ harness
296+ .defaultReplica
297+ .getRequests (SharedBackendReplicaHarness .METHOD_STREAMING_READ )
298+ .size ());
299+ ReadRequest replicaARequest =
300+ (ReadRequest )
301+ harness
302+ .replicas
303+ .get (0 )
304+ .getRequests (SharedBackendReplicaHarness .METHOD_STREAMING_READ )
305+ .get (0 );
306+ assertTrue (replicaARequest .getResumeToken ().isEmpty ());
307+ assertRetriedOnSameLogicalRequest (
308+ harness
309+ .replicas
310+ .get (0 )
311+ .getRequestIds (SharedBackendReplicaHarness .METHOD_STREAMING_READ )
312+ .get (0 ),
313+ harness
314+ .replicas
315+ .get (1 )
316+ .getRequestIds (SharedBackendReplicaHarness .METHOD_STREAMING_READ )
317+ .get (0 ));
318+ }
319+ }
320+
321+ @ Test
322+ public void singleUseReadCooldownSkipsUnavailableReplicaOnNextRequestForBypassTraffic ()
323+ throws Exception {
324+ try (SharedBackendReplicaHarness harness = SharedBackendReplicaHarness .create (2 );
325+ Spanner spanner = createSpanner (harness )) {
326+ configureBackend (harness , singleRowReadResultSet ("b" ));
327+ DatabaseClient client = spanner .getDatabaseClient (DatabaseId .of (PROJECT , INSTANCE , DATABASE ));
328+
329+ seedLocationMetadata (client );
330+ waitForReplicaRoutedRead (client , harness , 0 );
331+ harness .clearRequests ();
332+
333+ harness
334+ .replicas
335+ .get (0 )
336+ .putMethodErrors (
337+ SharedBackendReplicaHarness .METHOD_STREAMING_READ , unavailable ("isolated-replica" ));
338+
339+ try (ResultSet firstRead =
340+ client
341+ .singleUse ()
342+ .read (
343+ TABLE ,
344+ KeySet .singleKey (Key .of ("b" )),
345+ Arrays .asList ("k" ),
346+ Options .directedRead (DIRECTED_READ_OPTIONS ))) {
347+ assertTrue (firstRead .next ());
348+ }
349+
350+ try (ResultSet secondRead =
351+ client
352+ .singleUse ()
353+ .read (
354+ TABLE ,
355+ KeySet .singleKey (Key .of ("b" )),
356+ Arrays .asList ("k" ),
357+ Options .directedRead (DIRECTED_READ_OPTIONS ))) {
358+ assertTrue (secondRead .next ());
359+ }
360+
361+ assertEquals (
362+ 1 ,
363+ harness
364+ .replicas
365+ .get (0 )
366+ .getRequests (SharedBackendReplicaHarness .METHOD_STREAMING_READ )
367+ .size ());
368+ assertEquals (
369+ 2 ,
370+ harness
371+ .replicas
372+ .get (1 )
373+ .getRequests (SharedBackendReplicaHarness .METHOD_STREAMING_READ )
374+ .size ());
375+ assertEquals (
376+ 0 ,
377+ harness
378+ .defaultReplica
379+ .getRequests (SharedBackendReplicaHarness .METHOD_STREAMING_READ )
380+ .size ());
381+ List <AbstractMessage > replicaBRequests =
382+ harness .replicas .get (1 ).getRequests (SharedBackendReplicaHarness .METHOD_STREAMING_READ );
383+ for (AbstractMessage request : replicaBRequests ) {
384+ assertTrue (((ReadRequest ) request ).getResumeToken ().isEmpty ());
385+ }
386+ List <String > replicaBRequestIds =
387+ harness .replicas .get (1 ).getRequestIds (SharedBackendReplicaHarness .METHOD_STREAMING_READ );
388+ assertRetriedOnSameLogicalRequest (
389+ harness
390+ .replicas
391+ .get (0 )
392+ .getRequestIds (SharedBackendReplicaHarness .METHOD_STREAMING_READ )
393+ .get (0 ),
394+ replicaBRequestIds .get (0 ));
395+ assertNotEquals (
396+ XGoogSpannerRequestId .of (replicaBRequestIds .get (0 )).getLogicalRequestKey (),
397+ XGoogSpannerRequestId .of (replicaBRequestIds .get (1 )).getLogicalRequestKey ());
398+ }
399+ }
400+
251401 @ Test
252402 public void singleUseReadMidStreamRecvFailureWithoutRetryInfoRetriesForBypassTraffic ()
253403 throws Exception {
@@ -486,6 +636,10 @@ private static StatusRuntimeException resourceExhausted(String description) {
486636 return Status .RESOURCE_EXHAUSTED .withDescription (description ).asRuntimeException ();
487637 }
488638
639+ private static StatusRuntimeException unavailable (String description ) {
640+ return Status .UNAVAILABLE .withDescription (description ).asRuntimeException ();
641+ }
642+
489643 private static void assertRetriedOnSameLogicalRequest (
490644 String firstRequestId , String secondRequestId ) {
491645 XGoogSpannerRequestId first = XGoogSpannerRequestId .of (firstRequestId );
0 commit comments