diff --git a/d2/src/main/java/com/linkedin/d2/xds/XdsClient.java b/d2/src/main/java/com/linkedin/d2/xds/XdsClient.java index f5040dcb4..4167f1ffc 100644 --- a/d2/src/main/java/com/linkedin/d2/xds/XdsClient.java +++ b/d2/src/main/java/com/linkedin/d2/xds/XdsClient.java @@ -26,6 +26,7 @@ import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; +import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -183,15 +184,19 @@ final void onChanged(String resourceName, ResourceUpdate update) public interface ResourceUpdate { boolean isValid(); + @Nonnull + Map getVersions(); } public static final class NodeUpdate implements ResourceUpdate { XdsD2.Node _nodeData; + Map _versions; - NodeUpdate(XdsD2.Node nodeData) + NodeUpdate(XdsD2.Node nodeData, @Nonnull Map versions) { _nodeData = nodeData; + _versions = versions; } public XdsD2.Node getNodeData() @@ -211,13 +216,13 @@ public boolean equals(Object object) return false; } NodeUpdate that = (NodeUpdate) object; - return Objects.equals(_nodeData, that._nodeData); + return Objects.equals(_versions, that._versions) && Objects.equals(_nodeData, that._nodeData); } @Override public int hashCode() { - return Objects.hash(_nodeData); + return Objects.hash(_versions, _nodeData); } @Override @@ -226,20 +231,28 @@ public boolean isValid() return _nodeData != null && !_nodeData.getData().isEmpty(); } + @Nonnull + @Override + public Map getVersions() { + return _versions; + } + @Override public String toString() { - return MoreObjects.toStringHelper(this).add("_nodeData", _nodeData).toString(); + return MoreObjects.toStringHelper(this).add("_versions", _versions).add("_nodeData", _nodeData).toString(); } } public static final class D2URIMapUpdate implements ResourceUpdate { Map _uriMap; + Map _versions; - D2URIMapUpdate(Map uriMap) + D2URIMapUpdate(Map uriMap, @Nonnull Map versions) { _uriMap = uriMap; + _versions = versions; } public Map getURIMap() @@ -247,13 +260,14 @@ public Map getURIMap() return _uriMap; } - D2URIMapUpdate putUri(String name, XdsD2.D2URI uri) + D2URIMapUpdate putUri(String name, XdsD2.D2URI uri, String version) { if (_uriMap == null) { _uriMap = new HashMap<>(); } _uriMap.put(name, uri); + _versions.put(name, version); return this; } @@ -263,6 +277,7 @@ D2URIMapUpdate removeUri(String name) { _uriMap.remove(name); } + _versions.remove(name); return this; } @@ -278,13 +293,13 @@ public boolean equals(Object object) return false; } D2URIMapUpdate that = (D2URIMapUpdate) object; - return Objects.equals(_uriMap, that._uriMap); + return Objects.equals(_versions, that._versions) && Objects.equals(_uriMap, that._uriMap); } @Override public int hashCode() { - return Objects.hash(_uriMap); + return Objects.hash(_versions, _uriMap); } @Override @@ -293,15 +308,21 @@ public boolean isValid() return _uriMap != null; } + @Nonnull + @Override + public Map getVersions() { + return _versions; + } + @Override public String toString() { - return MoreObjects.toStringHelper(this).add("_uriMap", _uriMap).toString(); + return MoreObjects.toStringHelper(this).add("_versions", _versions).add("_uriMap", _uriMap).toString(); } } - public static final NodeUpdate EMPTY_NODE_UPDATE = new NodeUpdate(null); - public static final D2URIMapUpdate EMPTY_D2_URI_MAP_UPDATE = new D2URIMapUpdate(null); + public static final NodeUpdate EMPTY_NODE_UPDATE = new NodeUpdate(null, new HashMap<>()); + public static final D2URIMapUpdate EMPTY_D2_URI_MAP_UPDATE = new D2URIMapUpdate(null, new HashMap<>()); enum ResourceType { diff --git a/d2/src/main/java/com/linkedin/d2/xds/XdsClientImpl.java b/d2/src/main/java/com/linkedin/d2/xds/XdsClientImpl.java index 05e4a922a..55456c3e6 100644 --- a/d2/src/main/java/com/linkedin/d2/xds/XdsClientImpl.java +++ b/d2/src/main/java/com/linkedin/d2/xds/XdsClientImpl.java @@ -19,6 +19,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.MapDifference; import com.google.common.collect.Maps; import com.google.protobuf.InvalidProtocolBufferException; @@ -97,6 +98,7 @@ public class XdsClientImpl extends XdsClient private final XdsClientJmx _xdsClientJmx; private final XdsServerMetricsProvider _serverMetricsProvider; + private final Map _resourceVersions = new HashMap<>(); @Deprecated public XdsClientImpl(Node node, ManagedChannel managedChannel, ScheduledExecutorService executorService) @@ -167,7 +169,7 @@ public void watchXdsResource(String resourceName, ResourceWatcher watcher) } if (_adsStream != null) { - _adsStream.sendDiscoveryRequest(type, Collections.singletonList(adjustedResourceName)); + _adsStream.sendDiscoveryRequest(type, Collections.singletonList(adjustedResourceName), null); } } subscriber.addWatcher(watcher); @@ -198,7 +200,7 @@ public void watchAllXdsResources(WildcardResourceWatcher watcher) } if (_adsStream != null) { - _adsStream.sendDiscoveryRequest(adjustedType, Collections.singletonList("*")); + _adsStream.sendDiscoveryRequest(adjustedType, Collections.singletonList("*"), null); } } @@ -356,7 +358,7 @@ private void handleD2NodeResponse(DiscoveryResponseData data) { _log.warn("Received a Node response with no data, resource is : {}", resourceName); } - updates.put(resourceName, new NodeUpdate(d2Node)); + updates.put(resourceName, new NodeUpdate(d2Node, ImmutableMap.of(resourceName, resource.getVersion()))); } catch (InvalidProtocolBufferException e) { @@ -388,7 +390,7 @@ private void handleD2URIMapResponse(DiscoveryResponseData data) { _log.warn("Received a D2URIMap response with no data, resource is : {}", resourceName); } - updates.put(resourceName, new D2URIMapUpdate(nodeData)); + updates.put(resourceName, new D2URIMapUpdate(nodeData, ImmutableMap.of(resourceName, resource.getVersion()))); } catch (InvalidProtocolBufferException e) { @@ -453,11 +455,11 @@ private void handleD2URICollectionResponse(DiscoveryResponseData data) } if (currentData == null || !currentData.isValid()) { - return new D2URIMapUpdate(null); + return new D2URIMapUpdate(null, new HashMap<>()); } else { - return new D2URIMapUpdate(new HashMap<>(currentData.getURIMap())); + return new D2URIMapUpdate(new HashMap<>(currentData.getURIMap()), currentData.getVersions()); } }); @@ -481,7 +483,7 @@ private void handleD2URICollectionResponse(DiscoveryResponseData data) try { XdsD2.D2URI uri = resource.getResource().unpack(XdsD2.D2URI.class); - update.putUri(uriId.getUriName(), uri); + update.putUri(uriId.getUriName(), uri, resource.getVersion()); } catch (InvalidProtocolBufferException e) { @@ -866,7 +868,14 @@ public void run() startRpcStreamLocal(); for (ResourceType type : _resourceSubscribers.keySet()) { - Set resources = new HashSet<>(getResourceSubscriberMap(type).keySet()); + Map resourceVersions = new HashMap<>(); + Set resources = new HashSet<>(); + for (Map.Entry entry : getResourceSubscriberMap(type).entrySet()) { + resources.add(entry.getKey()); + if (entry.getValue() != null && entry.getValue().getData() != null) { + resourceVersions.putAll(entry.getValue().getData().getVersions()); + } + } if (resources.isEmpty() && getWildcardResourceSubscriber(type) == null) { continue; @@ -888,7 +897,7 @@ public void run() { resources.add("*"); } - _adsStream.sendDiscoveryRequest(rewrittenType, resources); + _adsStream.sendDiscoveryRequest(rewrittenType, resources, resourceVersions); } } } @@ -898,12 +907,16 @@ private static final class DiscoveryRequestData private final Node _node; private final ResourceType _resourceType; private final Collection _resourceNames; + @Nullable + private final Map _resourceVersions; - DiscoveryRequestData(Node node, ResourceType resourceType, Collection resourceNames) + DiscoveryRequestData(Node node, ResourceType resourceType, Collection resourceNames, + @Nullable Map resourceVersions) { _node = node; _resourceType = resourceType; _resourceNames = resourceNames; + _resourceVersions = resourceVersions; } DeltaDiscoveryRequest toEnvoyProto() @@ -912,7 +925,9 @@ DeltaDiscoveryRequest toEnvoyProto() .setNode(_node.toEnvoyProtoNode()) .addAllResourceNamesSubscribe(_resourceNames) .setTypeUrl(_resourceType.typeUrl()); - + if (_resourceVersions != null) { + builder.putAllInitialResourceVersions(_resourceVersions); + } return builder.build(); } @@ -1122,10 +1137,11 @@ public void onCompleted() /** * Sends a client-initiated discovery request. */ - private void sendDiscoveryRequest(ResourceType type, Collection resources) + private void sendDiscoveryRequest(ResourceType type, Collection resources, + @Nullable Map resourceVersions) { _log.info("Sending {} request for resources: {}", type, resources); - DeltaDiscoveryRequest request = new DiscoveryRequestData(_node, type, resources).toEnvoyProto(); + DeltaDiscoveryRequest request = new DiscoveryRequestData(_node, type, resources, resourceVersions).toEnvoyProto(); _requestWriter.onNext(request); _log.debug("Sent DiscoveryRequest\n{}", request); } diff --git a/d2/src/test/java/com/linkedin/d2/xds/TestXdsClientImpl.java b/d2/src/test/java/com/linkedin/d2/xds/TestXdsClientImpl.java index 76457deef..6c9bc1d2e 100644 --- a/d2/src/test/java/com/linkedin/d2/xds/TestXdsClientImpl.java +++ b/d2/src/test/java/com/linkedin/d2/xds/TestXdsClientImpl.java @@ -48,8 +48,8 @@ public class TestXdsClientImpl private static final Any PACKED_NODE_WITH_DATA = Any.pack(NODE_WITH_DATA); private static final Any PACKED_NODE_WITH_DATA2 = Any.pack(NODE_WITH_DATA2); private static final Any PACKED_NODE_WITH_EMPTY_DATA = Any.pack(NODE_WITH_EMPTY_DATA); - private static final XdsClient.NodeUpdate NODE_UPDATE1 = new XdsClient.NodeUpdate(NODE_WITH_DATA); - private static final XdsClient.NodeUpdate NODE_UPDATE2 = new XdsClient.NodeUpdate(NODE_WITH_DATA2); + private static final XdsClient.NodeUpdate NODE_UPDATE1 = new XdsClient.NodeUpdate(NODE_WITH_DATA, new HashMap<>()); + private static final XdsClient.NodeUpdate NODE_UPDATE2 = new XdsClient.NodeUpdate(NODE_WITH_DATA2, new HashMap<>()); private static final List NODE_RESOURCES_WITH_DATA1 = Collections.singletonList( Resource.newBuilder().setVersion(VERSION1).setName(SERVICE_RESOURCE_NAME).setResource(PACKED_NODE_WITH_DATA).build()); private static final List NODE_RESOURCES_WITH_DATA2 = Collections.singletonList( @@ -72,9 +72,9 @@ public class TestXdsClientImpl .putUris(URI1, D2URI_1_1) // updated uri1 .putUris(URI2, D2URI_2).build(); // added ur2 private static final D2URIMapUpdate D2_URI_MAP_UPDATE_WITH_DATA1 = - new D2URIMapUpdate(D2_URI_MAP_WITH_DATA1.getUrisMap()); + new D2URIMapUpdate(D2_URI_MAP_WITH_DATA1.getUrisMap(), new HashMap<>()); private static final D2URIMapUpdate D2_URI_MAP_UPDATE_WITH_DATA2 = - new D2URIMapUpdate(D2_URI_MAP_WITH_DATA2.getUrisMap()); + new D2URIMapUpdate(D2_URI_MAP_WITH_DATA2.getUrisMap(), new HashMap<>()); private static final Any PACKED_D2_URI_MAP_WITH_DATA1 = Any.pack(D2_URI_MAP_WITH_DATA1); private static final Any PACKED_D2_URI_MAP_WITH_DATA2 = Any.pack(D2_URI_MAP_WITH_DATA2); private static final Any PACKED_D2_URI_MAP_WITH_EMPTY_DATA = Any.pack(D2_URI_MAP_WITH_EMPTY_DATA); @@ -170,8 +170,8 @@ public void testHandleD2NodeResponseWithData() Assert.assertEquals(Objects.requireNonNull(actualData).getNodeData(), NODE_UPDATE1.getNodeData()); // subscriber original data is invalid, xds server latency won't be tracked - fixture._nodeSubscriber.setData(new XdsClient.NodeUpdate(null)); - fixture._nodeWildcardSubscriber.setData(SERVICE_RESOURCE_NAME, new XdsClient.NodeUpdate(null)); + fixture._nodeSubscriber.setData(new XdsClient.NodeUpdate(null, new HashMap<>())); + fixture._nodeWildcardSubscriber.setData(SERVICE_RESOURCE_NAME, new XdsClient.NodeUpdate(null, new HashMap<>())); fixture._xdsClientImpl.handleResponse(DISCOVERY_RESPONSE_NODE_DATA1); fixture.verifyAckSent(2); verify(fixture._resourceWatcher, times(2)).onChanged(eq(NODE_UPDATE1)); @@ -264,8 +264,8 @@ public void testHandleD2URIMapResponseWithData() Assert.assertEquals(Objects.requireNonNull(actualData).getURIMap(), D2_URI_MAP_UPDATE_WITH_DATA1.getURIMap()); // subscriber original data is invalid, xds server latency won't be tracked - fixture._clusterSubscriber.setData(new XdsClient.D2URIMapUpdate(null)); - fixture._uriMapWildcardSubscriber.setData(CLUSTER_RESOURCE_NAME, new XdsClient.D2URIMapUpdate(null)); + fixture._clusterSubscriber.setData(new XdsClient.D2URIMapUpdate(null, new HashMap<>())); + fixture._uriMapWildcardSubscriber.setData(CLUSTER_RESOURCE_NAME, new XdsClient.D2URIMapUpdate(null, new HashMap<>())); fixture._xdsClientImpl.handleResponse(DISCOVERY_RESPONSE_URI_MAP_DATA1); verify(fixture._resourceWatcher, times(2)).onChanged(eq(D2_URI_MAP_UPDATE_WITH_DATA1)); verify(fixture._wildcardResourceWatcher, times(2)).onChanged(eq(CLUSTER_RESOURCE_NAME), eq(D2_URI_MAP_UPDATE_WITH_DATA1)); @@ -312,7 +312,7 @@ public void testHandleD2URIMapUpdateWithBadData(DiscoveryResponseData badData, b D2URIMapUpdate expectedUpdate = invalidData ? (D2URIMapUpdate) D2_URI_MAP.emptyData() - : new D2URIMapUpdate(Collections.emptyMap()); + : new D2URIMapUpdate(Collections.emptyMap(), new HashMap<>()); verify(fixture._resourceWatcher).onChanged(eq(expectedUpdate)); if (!invalidData) { @@ -392,8 +392,8 @@ public void testHandleD2URICollectionResponseWithData() Assert.assertEquals(Objects.requireNonNull(actualData).getURIMap(), D2_URI_MAP_UPDATE_WITH_DATA1.getURIMap()); // subscriber original data is invalid, xds server latency won't be tracked - fixture._clusterSubscriber.setData(new D2URIMapUpdate(null)); - fixture._uriMapWildcardSubscriber.setData(CLUSTER_RESOURCE_NAME, new D2URIMapUpdate(null)); + fixture._clusterSubscriber.setData(new D2URIMapUpdate(null, new HashMap<>())); + fixture._uriMapWildcardSubscriber.setData(CLUSTER_RESOURCE_NAME, new D2URIMapUpdate(null, new HashMap<>())); fixture._xdsClientImpl.handleResponse(createUri1); fixture.verifyAckSent(2); verify(fixture._resourceWatcher, times(2)).onChanged(eq(D2_URI_MAP_UPDATE_WITH_DATA1)); @@ -410,7 +410,7 @@ public void testHandleD2URICollectionResponseWithData() fixture._xdsClientImpl.handleResponse(createUri2Delete1); actualData = (D2URIMapUpdate) fixture._clusterSubscriber.getData(); // subscriber data should be updated to D2_URI_MAP_UPDATE_WITH_DATA2 - D2URIMapUpdate expectedUpdate = new D2URIMapUpdate(Collections.singletonMap(URI2, D2URI_2)); + D2URIMapUpdate expectedUpdate = new D2URIMapUpdate(Collections.singletonMap(URI2, D2URI_2), new HashMap<>()); verify(fixture._resourceWatcher).onChanged(eq(expectedUpdate)); verify(fixture._wildcardResourceWatcher).onChanged(eq(CLUSTER_RESOURCE_NAME), eq(expectedUpdate)); // track latency only for updated/new uri (not for deletion) @@ -426,7 +426,7 @@ public void testHandleD2URICollectionResponseWithData() fixture._xdsClientImpl.handleResponse(deleteUri2); actualData = (D2URIMapUpdate) fixture._clusterSubscriber.getData(); // subscriber data should be updated to empty map - expectedUpdate = new D2URIMapUpdate(Collections.emptyMap()); + expectedUpdate = new D2URIMapUpdate(Collections.emptyMap(), new HashMap<>()); verify(fixture._resourceWatcher).onChanged(eq(expectedUpdate)); verify(fixture._wildcardResourceWatcher).onChanged(eq(CLUSTER_RESOURCE_NAME), eq(expectedUpdate)); verifyNoMoreInteractions(fixture._serverMetricsProvider); @@ -543,7 +543,7 @@ public void testResourceSubscriberAddWatcher() subscriber.addWatcher(watcher); verify(watcher, times(0)).onChanged(any()); - D2URIMapUpdate update = new D2URIMapUpdate(Collections.emptyMap()); + D2URIMapUpdate update = new D2URIMapUpdate(Collections.emptyMap(), new HashMap<>()); subscriber.setData(update); for (int i = 0; i < 10; i++) { diff --git a/d2/src/test/java/com/linkedin/d2/xds/TestXdsToD2PropertiesAdaptor.java b/d2/src/test/java/com/linkedin/d2/xds/TestXdsToD2PropertiesAdaptor.java index b630ecc86..31d93f82b 100644 --- a/d2/src/test/java/com/linkedin/d2/xds/TestXdsToD2PropertiesAdaptor.java +++ b/d2/src/test/java/com/linkedin/d2/xds/TestXdsToD2PropertiesAdaptor.java @@ -79,8 +79,8 @@ public class TestXdsToD2PropertiesAdaptor { private static final String SERVICE_NAME = "FooService"; private final UriPropertiesJsonSerializer _uriSerializer = new UriPropertiesJsonSerializer(); - private static final XdsClient.NodeUpdate EMPTY_NODE_DATA = new XdsClient.NodeUpdate(null); - private static final XdsClient.D2URIMapUpdate EMPTY_DATA_URI_MAP = new XdsClient.D2URIMapUpdate(null); + private static final XdsClient.NodeUpdate EMPTY_NODE_DATA = new XdsClient.NodeUpdate(null, new HashMap<>()); + private static final XdsClient.D2URIMapUpdate EMPTY_DATA_URI_MAP = new XdsClient.D2URIMapUpdate(null, new HashMap<>()); /* Provide { * @clientOverride transport port client properties set on client override @@ -139,7 +139,7 @@ public void testListenToService(Map clientOverride, Map()) ); verify(fixture._serviceEventBus).publishInitialize(serviceName, new ServiceStoreProperties(serviceName, PRIMARY_CLUSTER_NAME, "", @@ -228,7 +228,7 @@ public void testListenToNormalUri() throws PropertySerializationException verify(fixture._xdsClient, times(10)).watchXdsResource(eq(PRIMARY_URI_RESOURCE_NAME), anyMapWatcher()); XdsD2.D2URI protoUri = getD2URI(PRIMARY_CLUSTER_NAME, URI_NAME, VERSION); Map uriMap = new HashMap<>(Collections.singletonMap(URI_NAME, protoUri)); - fixture._uriMapWatcher.onChanged(new XdsClient.D2URIMapUpdate(uriMap)); + fixture._uriMapWatcher.onChanged(new XdsClient.D2URIMapUpdate(uriMap, new HashMap<>())); verify(fixture._uriEventBus).publishInitialize(PRIMARY_CLUSTER_NAME, _uriSerializer.fromProto(protoUri)); verify(fixture._eventEmitter).emitSDStatusInitialRequestEvent( eq(PRIMARY_CLUSTER_NAME), eq(true), anyLong(), eq(true)); @@ -238,7 +238,7 @@ public void testListenToNormalUri() throws PropertySerializationException // add uri 2 uriMap.put(URI_NAME_2, getD2URI(PRIMARY_CLUSTER_NAME, URI_NAME_2, VERSION)); - fixture._uriMapWatcher.onChanged(new XdsClient.D2URIMapUpdate(uriMap)); + fixture._uriMapWatcher.onChanged(new XdsClient.D2URIMapUpdate(uriMap, new HashMap<>())); verify(fixture._eventEmitter).emitSDStatusInitialRequestEvent( eq(PRIMARY_CLUSTER_NAME), eq(true), anyLong(), eq(true)); // no more initial request event emitted verify(fixture._eventEmitter).emitSDStatusUpdateReceiptEvent( // status update receipt event emitted for added uri @@ -249,7 +249,7 @@ public void testListenToNormalUri() throws PropertySerializationException uriMap.clear(); uriMap.put(URI_NAME, getD2URI(PRIMARY_CLUSTER_NAME, URI_NAME, VERSION_2)); uriMap.put(URI_NAME_3, getD2URI(PRIMARY_CLUSTER_NAME, URI_NAME_3, VERSION)); - fixture._uriMapWatcher.onChanged(new XdsClient.D2URIMapUpdate(uriMap)); + fixture._uriMapWatcher.onChanged(new XdsClient.D2URIMapUpdate(uriMap, new HashMap<>())); // events should be emitted only for remove/add, but not update verify(fixture._eventEmitter, never()).emitSDStatusUpdateReceiptEvent( any(), eq(HOST_1), anyInt(), eq(ServiceDiscoveryEventEmitter.StatusUpdateActionType.MARK_READY), anyBoolean(), @@ -286,7 +286,7 @@ public void testListenToUriSymlink() throws PropertySerializationException // update uri data D2URIMapResourceWatcher watcher = fixture._uriMapWatcher; - watcher.onChanged(new XdsClient.D2URIMapUpdate(Collections.emptyMap())); + watcher.onChanged(new XdsClient.D2URIMapUpdate(Collections.emptyMap(), new HashMap<>())); // verify uri data is merged and published under symlink name and the actual cluster name verify(fixture._uriEventBus).publishInitialize(SYMLINK_NAME, getDefaultUriProperties(SYMLINK_NAME)); @@ -308,7 +308,7 @@ public void testListenToUriSymlink() throws PropertySerializationException XdsD2.D2URI protoUri = getD2URI(PRIMARY_CLUSTER_NAME, LOCAL_HOST_URI.toString(), VERSION); UriProperties uriProps = new UriPropertiesJsonSerializer().fromProto(protoUri); - watcher.onChanged(new XdsClient.D2URIMapUpdate(Collections.singletonMap(URI_NAME, protoUri))); + watcher.onChanged(new XdsClient.D2URIMapUpdate(Collections.singletonMap(URI_NAME, protoUri), new HashMap<>())); verify(fixture._uriEventBus).publishInitialize(PRIMARY_CLUSTER_NAME, uriProps); // no status update receipt event emitted when data was empty before the update @@ -388,8 +388,7 @@ private static XdsClient.NodeUpdate getSymlinkNodeUpdate(String primaryClusterRe return new XdsClient.NodeUpdate( XdsD2.Node.newBuilder() .setData(ByteString.copyFromUtf8(primaryClusterResourceName)) - .build() - ); + .build(), new HashMap<>()); } private static XdsClient.NodeUpdate getClusterNodeUpdate(String clusterName) @@ -403,8 +402,7 @@ private static XdsClient.NodeUpdate getClusterNodeUpdate(String clusterName) ) ) .setStat(XdsD2.Stat.newBuilder().setMzxid(1L).build()) - .build() - ); + .build(), new HashMap<>()); } private void verifyClusterNodeUpdate(XdsToD2PropertiesAdaptorFixture fixture, String clusterName, String symlinkName, @@ -424,7 +422,7 @@ private void verifyUriUpdate(XdsToD2PropertiesAdaptorFixture fixture, String clu { D2URIMapResourceWatcher watcher = fixture._uriMapWatcher; XdsD2.D2URI protoUri = getD2URI(clusterName, LOCAL_HOST_URI.toString(), VERSION); - watcher.onChanged(new XdsClient.D2URIMapUpdate(Collections.singletonMap(URI_NAME, protoUri))); + watcher.onChanged(new XdsClient.D2URIMapUpdate(Collections.singletonMap(URI_NAME, protoUri), new HashMap<>())); verify(fixture._uriEventBus).publishInitialize(clusterName, _uriSerializer.fromProto(protoUri)); if (symlinkName != null) {