diff --git a/xds/src/main/java/io/grpc/xds/XdsDependencyManager.java b/xds/src/main/java/io/grpc/xds/XdsDependencyManager.java index c7333b93c1..82cfd9ccbd 100644 --- a/xds/src/main/java/io/grpc/xds/XdsDependencyManager.java +++ b/xds/src/main/java/io/grpc/xds/XdsDependencyManager.java @@ -36,6 +36,7 @@ import io.grpc.xds.client.XdsClient; import io.grpc.xds.client.XdsClient.ResourceWatcher; import io.grpc.xds.client.XdsResourceType; import java.util.Collections; +import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; @@ -53,8 +54,19 @@ import javax.annotation.Nullable; * applies to a single data plane authority. */ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegistry { - public static final XdsClusterResource CLUSTER_RESOURCE = XdsClusterResource.getInstance(); - public static final XdsEndpointResource ENDPOINT_RESOURCE = XdsEndpointResource.getInstance(); + private enum TrackedWatcherTypeEnum { + LDS, RDS, CDS, EDS + } + + private static final TrackedWatcherType LDS_TYPE = + new TrackedWatcherType<>(TrackedWatcherTypeEnum.LDS); + private static final TrackedWatcherType RDS_TYPE = + new TrackedWatcherType<>(TrackedWatcherTypeEnum.RDS); + private static final TrackedWatcherType CDS_TYPE = + new TrackedWatcherType<>(TrackedWatcherTypeEnum.CDS); + private static final TrackedWatcherType EDS_TYPE = + new TrackedWatcherType<>(TrackedWatcherTypeEnum.EDS); + private static final int MAX_CLUSTER_RECURSION_DEPTH = 16; // Specified by gRFC A37 private final String listenerName; private final XdsClient xdsClient; @@ -63,7 +75,8 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi private XdsConfigWatcher xdsConfigWatcher; private StatusOr lastUpdate = null; - private final Map, TypeWatchers> resourceWatchers = new HashMap<>(); + private final Map> resourceWatchers = + new EnumMap<>(TrackedWatcherTypeEnum.class); private final Set subscriptions = new HashSet<>(); XdsDependencyManager(XdsClient xdsClient, @@ -86,7 +99,7 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi checkState(this.xdsConfigWatcher == null, "dep manager may not be restarted"); this.xdsConfigWatcher = checkNotNull(xdsConfigWatcher, "xdsConfigWatcher"); // start the ball rolling - syncContext.execute(() -> addWatcher(new LdsWatcher(listenerName))); + syncContext.execute(() -> addWatcher(LDS_TYPE, new LdsWatcher(listenerName))); } @Override @@ -96,7 +109,7 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi ClusterSubscription subscription = new ClusterSubscription(clusterName); syncContext.execute(() -> { - if (getWatchers(XdsListenerResource.getInstance()).isEmpty()) { + if (getWatchers(LDS_TYPE).isEmpty()) { subscription.closed = true; return; // shutdown() called } @@ -107,33 +120,28 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi return subscription; } - private void addWatcher(XdsWatcherBase watcher) { + private void addWatcher( + TrackedWatcherType watcherType, XdsWatcherBase watcher) { syncContext.throwIfNotInThisSynchronizationContext(); XdsResourceType type = watcher.type; String resourceName = watcher.resourceName; - getWatchers(type).put(resourceName, watcher); + getWatchers(watcherType).put(resourceName, watcher); xdsClient.watchXdsResource(type, resourceName, watcher, syncContext); } public void shutdown() { syncContext.execute(() -> { for (TypeWatchers watchers : resourceWatchers.values()) { - shutdownWatchersForType(watchers); + for (TrackedWatcher watcher : watchers.watchers.values()) { + watcher.close(); + } } resourceWatchers.clear(); subscriptions.clear(); }); } - private void shutdownWatchersForType(TypeWatchers watchers) { - for (Map.Entry> watcherEntry : watchers.watchers.entrySet()) { - xdsClient.cancelXdsResourceWatch(watchers.resourceType, watcherEntry.getKey(), - watcherEntry.getValue()); - watcherEntry.getValue().cancelled = true; - } - } - private void releaseSubscription(ClusterSubscription subscription) { checkNotNull(subscription, "subscription"); syncContext.execute(() -> { @@ -154,12 +162,12 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi */ private void maybePublishConfig() { syncContext.throwIfNotInThisSynchronizationContext(); - if (getWatchers(XdsListenerResource.getInstance()).isEmpty()) { + if (getWatchers(LDS_TYPE).isEmpty()) { return; // shutdown() called } boolean waitingOnResource = resourceWatchers.values().stream() .flatMap(typeWatchers -> typeWatchers.watchers.values().stream()) - .anyMatch(XdsWatcherBase::missingResult); + .anyMatch(TrackedWatcher::missingResult); if (waitingOnResource) { return; } @@ -194,8 +202,8 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi // Iterate watchers and build the XdsConfig - XdsWatcherBase ldsWatcher - = tracer.getWatcher(XdsListenerResource.getInstance(), listenerName); + TrackedWatcher ldsWatcher + = tracer.getWatcher(LDS_TYPE, listenerName); if (ldsWatcher == null) { return StatusOr.fromStatus(Status.UNAVAILABLE.withDescription( "Bug: No listener watcher found for " + listenerName)); @@ -241,14 +249,13 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi return StatusOr.fromValue(builder.build()); } - private Map> getWatchers( - XdsResourceType resourceType) { - TypeWatchers typeWatchers = resourceWatchers.get(resourceType); + private Map> getWatchers(TrackedWatcherType watcherType) { + TypeWatchers typeWatchers = resourceWatchers.get(watcherType.typeEnum); if (typeWatchers == null) { - typeWatchers = new TypeWatchers(resourceType); - resourceWatchers.put(resourceType, typeWatchers); + typeWatchers = new TypeWatchers(watcherType); + resourceWatchers.put(watcherType.typeEnum, typeWatchers); } - assert typeWatchers.resourceType == resourceType; + assert typeWatchers.watcherType == watcherType; @SuppressWarnings("unchecked") TypeWatchers tTypeWatchers = (TypeWatchers) typeWatchers; return tTypeWatchers.watchers; @@ -275,7 +282,7 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi return; } - CdsWatcher cdsWatcher = (CdsWatcher) tracer.getWatcher(CLUSTER_RESOURCE, clusterName); + CdsWatcher cdsWatcher = (CdsWatcher) tracer.getWatcher(CDS_TYPE, clusterName); StatusOr cdsWatcherDataOr = cdsWatcher.getData(); if (!cdsWatcherDataOr.hasValue()) { clusters.put(clusterName, StatusOr.fromStatus(cdsWatcherDataOr.getStatus())); @@ -318,8 +325,8 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi child = new AggregateConfig(ImmutableList.copyOf(leafNames)); break; case EDS: - XdsWatcherBase edsWatcher = - tracer.getWatcher(ENDPOINT_RESOURCE, cdsWatcher.getEdsServiceName()); + TrackedWatcher edsWatcher = + tracer.getWatcher(EDS_TYPE, cdsWatcher.getEdsServiceName()); if (edsWatcher != null) { child = new EndpointConfig(edsWatcher.getData()); } else { @@ -346,27 +353,27 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi } private void addRdsWatcher(String resourceName) { - if (getWatchers(XdsRouteConfigureResource.getInstance()).containsKey(resourceName)) { + if (getWatchers(RDS_TYPE).containsKey(resourceName)) { return; } - addWatcher(new RdsWatcher(resourceName)); + addWatcher(RDS_TYPE, new RdsWatcher(resourceName)); } private void addEdsWatcher(String edsServiceName) { - if (getWatchers(XdsEndpointResource.getInstance()).containsKey(edsServiceName)) { + if (getWatchers(EDS_TYPE).containsKey(edsServiceName)) { return; } - addWatcher(new EdsWatcher(edsServiceName)); + addWatcher(EDS_TYPE, new EdsWatcher(edsServiceName)); } private void addClusterWatcher(String clusterName) { - if (getWatchers(CLUSTER_RESOURCE).containsKey(clusterName)) { + if (getWatchers(CDS_TYPE).containsKey(clusterName)) { return; } - addWatcher(new CdsWatcher(clusterName)); + addWatcher(CDS_TYPE, new CdsWatcher(clusterName)); } private void updateRoutes(List virtualHosts) { @@ -404,13 +411,13 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi return clusters; } - private static class TypeWatchers { + private static class TypeWatchers { // Key is resource name - final Map> watchers = new HashMap<>(); - final XdsResourceType resourceType; + final Map> watchers = new HashMap<>(); + final TrackedWatcherType watcherType; - TypeWatchers(XdsResourceType resourceType) { - this.resourceType = resourceType; + TypeWatchers(TrackedWatcherType watcherType) { + this.watcherType = checkNotNull(watcherType, "watcherType"); } } @@ -442,38 +449,36 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi /** State for tracing garbage collector. */ private static final class WatcherTracer { - private final Map, TypeWatchers> resourceWatchers; - private final Map, TypeWatchers> usedWatchers; + private final Map> resourceWatchers; + private final Map> usedWatchers; - public WatcherTracer(Map, TypeWatchers> resourceWatchers) { + public WatcherTracer(Map> resourceWatchers) { this.resourceWatchers = resourceWatchers; - this.usedWatchers = new HashMap<>(); - for (XdsResourceType type : resourceWatchers.keySet()) { - usedWatchers.put(type, newTypeWatchers(type)); + this.usedWatchers = new EnumMap<>(TrackedWatcherTypeEnum.class); + for (Map.Entry> me : resourceWatchers.entrySet()) { + usedWatchers.put(me.getKey(), newTypeWatchers(me.getValue().watcherType)); } } - private static TypeWatchers newTypeWatchers( - XdsResourceType type) { + private static TypeWatchers newTypeWatchers(TrackedWatcherType type) { return new TypeWatchers(type); } - public XdsWatcherBase getWatcher( - XdsResourceType resourceType, String name) { - TypeWatchers typeWatchers = resourceWatchers.get(resourceType); + public TrackedWatcher getWatcher(TrackedWatcherType watcherType, String name) { + TypeWatchers typeWatchers = resourceWatchers.get(watcherType.typeEnum); if (typeWatchers == null) { return null; } - assert typeWatchers.resourceType == resourceType; + assert typeWatchers.watcherType == watcherType; @SuppressWarnings("unchecked") TypeWatchers tTypeWatchers = (TypeWatchers) typeWatchers; - XdsWatcherBase watcher = tTypeWatchers.watchers.get(name); + TrackedWatcher watcher = tTypeWatchers.watchers.get(name); if (watcher == null) { return null; } @SuppressWarnings("unchecked") - TypeWatchers usedTypeWatchers = (TypeWatchers) usedWatchers.get(resourceType); + TypeWatchers usedTypeWatchers = (TypeWatchers) usedWatchers.get(watcherType.typeEnum); usedTypeWatchers.watchers.put(name, watcher); return watcher; } @@ -481,9 +486,9 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi /** Shut down unused watchers. */ public void closeUnusedWatchers() { boolean changed = false; // Help out the GC by preferring old objects - for (XdsResourceType type : resourceWatchers.keySet()) { - TypeWatchers orig = resourceWatchers.get(type); - TypeWatchers used = usedWatchers.get(type); + for (TrackedWatcherTypeEnum key : resourceWatchers.keySet()) { + TypeWatchers orig = resourceWatchers.get(key); + TypeWatchers used = usedWatchers.get(key); for (String name : orig.watchers.keySet()) { if (used.watchers.containsKey(name)) { continue; @@ -498,8 +503,33 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi } } + @SuppressWarnings("UnusedTypeParameter") + private static final class TrackedWatcherType { + public final TrackedWatcherTypeEnum typeEnum; + + public TrackedWatcherType(TrackedWatcherTypeEnum typeEnum) { + this.typeEnum = checkNotNull(typeEnum, "typeEnum"); + } + } + + private interface TrackedWatcher { + @Nullable + StatusOr getData(); + + default boolean missingResult() { + return getData() == null; + } + + default boolean hasDataValue() { + StatusOr data = getData(); + return data != null && data.hasValue(); + } + + void close(); + } + private abstract class XdsWatcherBase - implements ResourceWatcher { + implements ResourceWatcher, TrackedWatcher { private final XdsResourceType type; private final String resourceName; boolean cancelled; @@ -554,24 +584,18 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi protected abstract void subscribeToChildren(T update); + @Override public void close() { cancelled = true; xdsClient.cancelXdsResourceWatch(type, resourceName, this); } - boolean missingResult() { - return data == null; - } - + @Override @Nullable - StatusOr getData() { + public StatusOr getData() { return data; } - boolean hasDataValue() { - return data != null && data.hasValue(); - } - public String toContextString() { return toContextStr(type.typeName(), resourceName); } @@ -622,7 +646,7 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi if (rdsName == null) { return null; } - return (RdsWatcher) tracer.getWatcher(XdsRouteConfigureResource.getInstance(), rdsName); + return (RdsWatcher) tracer.getWatcher(RDS_TYPE, rdsName); } public RdsUpdateSupplier getRouteSource(WatcherTracer tracer) { @@ -688,7 +712,7 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi private class CdsWatcher extends XdsWatcherBase { CdsWatcher(String resourceName) { - super(CLUSTER_RESOURCE, checkNotNull(resourceName, "resourceName")); + super(XdsClusterResource.getInstance(), checkNotNull(resourceName, "resourceName")); } @Override @@ -721,7 +745,7 @@ final class XdsDependencyManager implements XdsConfig.XdsClusterSubscriptionRegi private class EdsWatcher extends XdsWatcherBase { private EdsWatcher(String resourceName) { - super(ENDPOINT_RESOURCE, checkNotNull(resourceName, "resourceName")); + super(XdsEndpointResource.getInstance(), checkNotNull(resourceName, "resourceName")); } @Override