@@ -428,7 +428,6 @@ private void verifyResourceCount(
428428 XdsResourceType <?> type ,
429429 int size ) {
430430 if (size == 0 ) {
431- assertThat (subscribedTypeUrls .containsKey (type .typeUrl ())).isFalse ();
432431 assertThat (subscribedResourcesMetadata .containsKey (type )).isFalse ();
433432 } else {
434433 assertThat (subscribedTypeUrls .containsKey (type .typeUrl ())).isTrue ();
@@ -2614,58 +2613,6 @@ public void cdsResourceDeleted() {
26142613 verifySubscribedResourcesMetadataSizes (0 , 1 , 0 , 0 );
26152614 }
26162615
2617- @ Test
2618- public void cdsResourcesDelete_edsUnsubscribed () {
2619- Assume .assumeFalse (ignoreResourceDeletion ());
2620-
2621- List <String > subscribedResourceNames = ImmutableList .of ("A" );
2622- xdsClient .watchXdsResource (XdsClusterResource .getInstance (), "A" , cdsResourceWatcher );
2623- xdsClient .watchXdsResource (XdsEndpointResource .getInstance (), "A.1" , edsResourceWatcher );
2624- DiscoveryRpcCall call = resourceDiscoveryCalls .poll ();
2625- assertThat (call ).isNotNull ();
2626- verifyResourceMetadataRequested (CDS , "A" );
2627- verifyResourceMetadataRequested (EDS , "A.1" );
2628- verifySubscribedResourcesMetadataSizes (0 , 1 , 0 , 1 );
2629-
2630- // CDS -> {A}, version 1
2631- ImmutableMap <String , Any > resourcesV1 = ImmutableMap .of (
2632- "A" , Any .pack (mf .buildEdsCluster ("A" , "A.1" , "round_robin" , null , null , false , null ,
2633- "envoy.transport_sockets.tls" , null , null
2634- )));
2635- call .sendResponse (CDS , resourcesV1 .values ().asList (), VERSION_1 , "0000" );
2636- // {A, B} -> ACK, version 1
2637- verifyResourceMetadataAcked (CDS , "A" , resourcesV1 .get ("A" ), VERSION_1 , TIME_INCREMENT );
2638- call .verifyRequest (CDS , subscribedResourceNames , VERSION_1 , "0000" , NODE );
2639-
2640- // EDS -> {A.1}, version 1
2641- List <Message > dropOverloads = ImmutableList .of ();
2642- List <Message > endpointsV1 = ImmutableList .of (lbEndpointHealthy );
2643- ImmutableMap <String , Any > resourcesV11 = ImmutableMap .of (
2644- "A.1" , Any .pack (mf .buildClusterLoadAssignment ("A.1" , endpointsV1 , dropOverloads )));
2645- call .sendResponse (EDS , resourcesV11 .values ().asList (), VERSION_1 , "0000" );
2646- // {A.1} -> ACK, version 1
2647- verifyResourceMetadataAcked (EDS , "A.1" , resourcesV11 .get ("A.1" ), VERSION_1 , TIME_INCREMENT * 2 );
2648- verify (cdsResourceWatcher , times (1 )).onChanged (any ());
2649-
2650- // Empty CDS response deletes the cluster.
2651- call .sendResponse (CDS , Collections .<Any >emptyList (), VERSION_2 , "0001" );
2652- call .verifyRequest (CDS , "A" , VERSION_2 , "0001" , NODE );
2653- verify (cdsResourceWatcher ).onResourceDoesNotExist ("A" );
2654- verifyResourceMetadataDoesNotExist (CDS , "A" );
2655- verifySubscribedResourcesMetadataSizes (0 , 1 , 0 , 1 );
2656- // Empty CDS leads to EDS resource "A.1" unsubscribed.
2657- xdsClient .cancelXdsResourceWatch (XdsEndpointResource .getInstance (), "A.1" , edsResourceWatcher );
2658- verifySubscribedResourcesMetadataSizes (0 , 1 , 0 , 0 );
2659-
2660- // Send any EDS will not trigger any ACK/NACK response
2661- Any updatedClusterLoadAssignment = Any .pack (mf .buildClusterLoadAssignment ("A.1" ,
2662- ImmutableList .of (mf .buildLocalityLbEndpoints ("region2" , "zone2" , "subzone2" ,
2663- mf .buildLbEndpoint ("172.44.2.2" , 8000 , "unknown" , 3 ), 2 , 0 )),
2664- ImmutableList .<Message >of ()));
2665- call .sendResponse (EDS , updatedClusterLoadAssignment , VERSION_2 , "0001" );
2666- call .verifyNoMoreRequest ();
2667- }
2668-
26692616 /**
26702617 * When ignore_resource_deletion server feature is on, xDS client should keep the deleted cluster
26712618 * on empty response, and resume the normal work when CDS contains the cluster again.
@@ -2809,6 +2756,44 @@ public void edsResourceNotFound() {
28092756 verifySubscribedResourcesMetadataSizes (0 , 0 , 0 , 1 );
28102757 }
28112758
2759+ @ Test
2760+ public void edsAllowRespondAfterUnsubscription () {
2761+ Assume .assumeFalse (ignoreResourceDeletion ());
2762+
2763+ // Suppose we have an EDS subscription A.1
2764+ List <String > subscribedResourceNames = ImmutableList .of ("A.1" );
2765+ xdsClient .watchXdsResource (XdsEndpointResource .getInstance (), "A.1" , edsResourceWatcher );
2766+ DiscoveryRpcCall call = resourceDiscoveryCalls .poll ();
2767+ assertThat (call ).isNotNull ();
2768+ verifyResourceMetadataRequested (EDS , "A.1" );
2769+ verifySubscribedResourcesMetadataSizes (0 , 0 , 0 , 1 );
2770+
2771+ // EDS -> {A.1}, version 1
2772+ List <Message > dropOverloads = ImmutableList .of ();
2773+ List <Message > endpointsV1 = ImmutableList .of (lbEndpointHealthy );
2774+ ImmutableMap <String , Any > resourcesV1 = ImmutableMap .of (
2775+ "A.1" , Any .pack (mf .buildClusterLoadAssignment ("A.1" , endpointsV1 , dropOverloads )));
2776+ call .sendResponse (EDS , resourcesV1 .values ().asList (), VERSION_1 , "0000" );
2777+ // {A.1} -> ACK, version 1
2778+ verifyResourceMetadataAcked (EDS , "A.1" , resourcesV1 .get ("A.1" ), VERSION_1 , TIME_INCREMENT );
2779+ verify (edsResourceWatcher , times (1 )).onChanged (any ());
2780+
2781+ // trigger an EDS resource unsubscription. This would probably be caused by CDS PUSH(let's say event e1) in the real world.
2782+ // Then there can be a potential data race between
2783+ // 1) the EDS unsubscription caused by CDS PUSH e1 (client-side) and,
2784+ // 2) the immediate EDS PUSH from XdsServer (server-side) after CDS PUSH e1 (event e2).
2785+ xdsClient .cancelXdsResourceWatch (XdsEndpointResource .getInstance (), "A.1" , edsResourceWatcher );
2786+ verifySubscribedResourcesMetadataSizes (0 , 0 , 0 , 0 );
2787+ // An EDS PUSH after CDS PUSH e1.
2788+ List <Message > endpointsV2 = ImmutableList .of (lbEndpointHealthy );
2789+ ImmutableMap <String , Any > resourcesV2 = ImmutableMap .of (
2790+ "A.1" , Any .pack (mf .buildClusterLoadAssignment ("A.1" , endpointsV2 , dropOverloads )));
2791+ call .sendResponse (EDS , resourcesV2 .values ().asList (), VERSION_2 , "0001" );
2792+ // This will send an empty resource list to the XdsServer
2793+ call .verifyRequest (EDS , Collections .emptyList (), VERSION_2 , "0001" , NODE );
2794+ verifyNoMoreInteractions (edsResourceWatcher );
2795+ }
2796+
28122797 @ Test
28132798 public void edsResponseErrorHandling_allResourcesFailedUnpack () {
28142799 DiscoveryRpcCall call = startResourceWatcher (XdsEndpointResource .getInstance (), EDS_RESOURCE ,
0 commit comments