From 82ca98f3386a3b13228128c4637e0eb83d7db182 Mon Sep 17 00:00:00 2001 From: yifeizhuang Date: Wed, 11 Jan 2023 15:33:48 -0800 Subject: [PATCH] xds:fix cancel xds watcher accidentally removes the url (#9809) Fix a bug. When any of the xds subscribers for a resource has the last watcher cancelled, the bug will accidentally remove that resource type from the map, which make xds stream not accepting response update for that resource type entirely(pass through, no ACK/NACK will send). --- .../main/java/io/grpc/xds/XdsClientImpl.java | 3 +- .../io/grpc/xds/XdsClientImplTestBase.java | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/xds/src/main/java/io/grpc/xds/XdsClientImpl.java b/xds/src/main/java/io/grpc/xds/XdsClientImpl.java index a79fc6bfb91..e54c52ec295 100644 --- a/xds/src/main/java/io/grpc/xds/XdsClientImpl.java +++ b/xds/src/main/java/io/grpc/xds/XdsClientImpl.java @@ -332,12 +332,13 @@ public void run() { if (!subscriber.isWatched()) { subscriber.cancelResourceWatch(); resourceSubscribers.get(type).remove(resourceName); - subscribedResourceTypeUrls.remove(type.typeUrl()); if (subscriber.xdsChannel != null) { subscriber.xdsChannel.adjustResourceSubscription(type); } if (resourceSubscribers.get(type).isEmpty()) { resourceSubscribers.remove(type); + subscribedResourceTypeUrls.remove(type.typeUrl()); + } } } diff --git a/xds/src/test/java/io/grpc/xds/XdsClientImplTestBase.java b/xds/src/test/java/io/grpc/xds/XdsClientImplTestBase.java index 138ab4f589b..3ee6c916c2e 100644 --- a/xds/src/test/java/io/grpc/xds/XdsClientImplTestBase.java +++ b/xds/src/test/java/io/grpc/xds/XdsClientImplTestBase.java @@ -938,6 +938,35 @@ public void ldsResourceUpdated() { assertThat(channelForEmptyAuthority).isNull(); } + @Test + public void cancelResourceWatcherNotRemoveUrlSubscribers() { + DiscoveryRpcCall call = startResourceWatcher(XdsListenerResource.getInstance(), LDS_RESOURCE, + ldsResourceWatcher); + verifyResourceMetadataRequested(LDS, LDS_RESOURCE); + + // Initial LDS response. + call.sendResponse(LDS, testListenerVhosts, VERSION_1, "0000"); + call.verifyRequest(LDS, LDS_RESOURCE, VERSION_1, "0000", NODE); + verify(ldsResourceWatcher).onChanged(ldsUpdateCaptor.capture()); + verifyGoldenListenerVhosts(ldsUpdateCaptor.getValue()); + verifyResourceMetadataAcked(LDS, LDS_RESOURCE, testListenerVhosts, VERSION_1, TIME_INCREMENT); + + xdsClient.watchXdsResource(XdsListenerResource.getInstance(), + LDS_RESOURCE + "1", ldsResourceWatcher); + xdsClient.cancelXdsResourceWatch(XdsListenerResource.getInstance(), LDS_RESOURCE + "1", + ldsResourceWatcher); + + // Updated LDS response. + Any testListenerVhosts2 = Any.pack(mf.buildListenerWithApiListener(LDS_RESOURCE, + mf.buildRouteConfiguration("new", mf.buildOpaqueVirtualHosts(VHOST_SIZE)))); + call.sendResponse(LDS, testListenerVhosts2, VERSION_2, "0001"); + call.verifyRequest(LDS, LDS_RESOURCE, VERSION_2, "0001", NODE); + verify(ldsResourceWatcher).onChanged(ldsUpdateCaptor.capture()); + verifyGoldenListenerVhosts(ldsUpdateCaptor.getValue()); + verifyResourceMetadataAcked(LDS, LDS_RESOURCE, testListenerVhosts2, VERSION_2, + TIME_INCREMENT * 2); + } + @Test public void ldsResourceUpdated_withXdstpResourceName() { BootstrapperImpl.enableFederation = true;