2525import static org .mockito .Mockito .CALLS_REAL_METHODS ;
2626import static org .mockito .Mockito .inOrder ;
2727import static org .mockito .Mockito .mock ;
28- import static org .mockito .Mockito .never ;
2928import static org .mockito .Mockito .times ;
3029import static org .mockito .Mockito .verify ;
3130
4140import io .grpc .ForwardingChannelBuilder ;
4241import io .grpc .LoadBalancer ;
4342import io .grpc .LoadBalancer .Helper ;
43+ import io .grpc .LoadBalancer .PickResult ;
4444import io .grpc .LoadBalancer .SubchannelPicker ;
4545import io .grpc .LoadBalancerProvider ;
4646import io .grpc .ManagedChannel ;
4747import io .grpc .ManagedChannelBuilder ;
4848import io .grpc .Metadata ;
4949import io .grpc .NameResolver ;
50+ import io .grpc .NameResolver .ConfigOrError ;
5051import io .grpc .Status ;
52+ import io .grpc .Status .Code ;
5153import io .grpc .SynchronizationContext ;
5254import io .grpc .inprocess .InProcessChannelBuilder ;
5355import io .grpc .inprocess .InProcessServerBuilder ;
@@ -102,6 +104,7 @@ public class CachingRlsLbClientTest {
102104
103105 private static final RouteLookupConfig ROUTE_LOOKUP_CONFIG = getRouteLookupConfig ();
104106 private static final int SERVER_LATENCY_MILLIS = 10 ;
107+ private static final String DEFAULT_TARGET = "fallback.cloudbigtable.googleapis.com" ;
105108
106109 @ Rule
107110 public final MockitoRule mocks = MockitoJUnit .rule ();
@@ -278,7 +281,9 @@ public void get_updatesLbState() throws Exception {
278281 rlsServerImpl .setLookupTable (
279282 ImmutableMap .of (
280283 routeLookupRequest ,
281- new RouteLookupResponse (ImmutableList .of ("target" ), "header-rls-data-value" )));
284+ new RouteLookupResponse (
285+ ImmutableList .of ("primary.cloudbigtable.googleapis.com" ),
286+ "header-rls-data-value" )));
282287
283288 // valid channel
284289 CachedRouteLookupResponse resp = getInSyncContext (routeLookupRequest );
@@ -298,28 +303,41 @@ public void get_updatesLbState() throws Exception {
298303 assertThat (stateCaptor .getAllValues ())
299304 .containsExactly (ConnectivityState .CONNECTING , ConnectivityState .READY );
300305 Metadata headers = new Metadata ();
301- pickerCaptor .getValue ().pickSubchannel (
306+ PickResult pickResult = pickerCaptor .getValue ().pickSubchannel (
302307 new PickSubchannelArgsImpl (
303308 TestMethodDescriptors .voidMethod ().toBuilder ().setFullMethodName ("foo/bar" ).build (),
304309 headers ,
305310 CallOptions .DEFAULT ));
311+ assertThat (pickResult .getStatus ().isOk ()).isTrue ();
312+ assertThat (pickResult .getSubchannel ()).isNotNull ();
306313 assertThat (headers .get (RLS_DATA_KEY )).isEqualTo ("header-rls-data-value" );
307314
308315 // move backoff further back to only test error behavior
309316 fakeBackoffProvider .nextPolicy = createBackoffPolicy (100 , TimeUnit .MILLISECONDS );
310317 // try to get invalid
311318 RouteLookupRequest invalidRouteLookupRequest =
312319 new RouteLookupRequest (
313- "unknown_server " , "/doesn/exists" , "grpc" , ImmutableMap .<String , String >of ());
320+ "service1 " , "/doesn/exists" , "grpc" , ImmutableMap .<String , String >of ());
314321 CachedRouteLookupResponse errorResp = getInSyncContext (invalidRouteLookupRequest );
315322 assertThat (errorResp .isPending ()).isTrue ();
316323 fakeTimeProvider .forwardTime (SERVER_LATENCY_MILLIS , TimeUnit .MILLISECONDS );
317324
318325 errorResp = getInSyncContext (invalidRouteLookupRequest );
319326 assertThat (errorResp .hasError ()).isTrue ();
320327
321- inOrder .verify (helper , never ())
322- .updateBalancingState (any (ConnectivityState .class ), any (SubchannelPicker .class ));
328+ // Channel is still READY because the subchannel for method /foo/bar is still READY.
329+ // Method /doesn/exists will use fallback child balancer and fail immediately.
330+ inOrder .verify (helper )
331+ .updateBalancingState (eq (ConnectivityState .READY ), pickerCaptor .capture ());
332+ pickResult = pickerCaptor .getValue ().pickSubchannel (
333+ new PickSubchannelArgsImpl (
334+ TestMethodDescriptors .voidMethod ().toBuilder ()
335+ .setFullMethodName ("doesn/exists" )
336+ .build (),
337+ headers ,
338+ CallOptions .DEFAULT ));
339+ assertThat (pickResult .getStatus ().getCode ()).isEqualTo (Code .UNAVAILABLE );
340+ assertThat (pickResult .getStatus ().getDescription ()).isEqualTo ("fallback not available" );
323341 }
324342
325343 @ Test
@@ -370,7 +388,7 @@ private static RouteLookupConfig getRouteLookupConfig() {
370388 /* staleAgeInMillis= */ TimeUnit .SECONDS .toMillis (240 ),
371389 /* cacheSizeBytes= */ 1000 ,
372390 /* validTargets= */ ImmutableList .of ("a valid target" ),
373- /* defaultTarget= */ "us_east_1.cloudbigtable.googleapis.com" );
391+ DEFAULT_TARGET );
374392 }
375393
376394 private static BackoffPolicy createBackoffPolicy (final long delay , final TimeUnit unit ) {
@@ -395,6 +413,10 @@ public BackoffPolicy get() {
395413 }
396414 }
397415
416+ /**
417+ * A load balancer that immediately goes to READY when using the rls response target and
418+ * immediately fails when using the fallback target.
419+ */
398420 private static final class TestLoadBalancerProvider extends LoadBalancerProvider {
399421
400422 @ Override
@@ -412,21 +434,39 @@ public String getPolicyName() {
412434 return null ;
413435 }
414436
437+ @ Override
438+ public ConfigOrError parseLoadBalancingPolicyConfig (
439+ Map <String , ?> rawLoadBalancingPolicyConfig ) {
440+ return ConfigOrError .fromConfig (rawLoadBalancingPolicyConfig );
441+ }
442+
415443 @ Override
416444 public LoadBalancer newLoadBalancer (final Helper helper ) {
417445 return new LoadBalancer () {
418446
419447 @ Override
420448 public void handleResolvedAddresses (ResolvedAddresses resolvedAddresses ) {
421- // TODO: make the picker accessible
422- helper .updateBalancingState (
423- ConnectivityState .READY ,
424- new SubchannelPicker () {
425- @ Override
426- public PickResult pickSubchannel (PickSubchannelArgs args ) {
427- return PickResult .withSubchannel (mock (Subchannel .class ));
428- }
429- });
449+ Map <?, ?> config = (Map <?, ?>) resolvedAddresses .getLoadBalancingPolicyConfig ();
450+ if (DEFAULT_TARGET .equals (config .get ("target" ))) {
451+ helper .updateBalancingState (
452+ ConnectivityState .TRANSIENT_FAILURE ,
453+ new SubchannelPicker () {
454+ @ Override
455+ public PickResult pickSubchannel (PickSubchannelArgs args ) {
456+ return PickResult .withError (
457+ Status .UNAVAILABLE .withDescription ("fallback not available" ));
458+ }
459+ });
460+ } else {
461+ helper .updateBalancingState (
462+ ConnectivityState .READY ,
463+ new SubchannelPicker () {
464+ @ Override
465+ public PickResult pickSubchannel (PickSubchannelArgs args ) {
466+ return PickResult .withSubchannel (mock (Subchannel .class ));
467+ }
468+ });
469+ }
430470 }
431471
432472 @ Override
0 commit comments