@@ -251,8 +251,10 @@ private void releaseSubscription(ClusterSubscription subscription) {
251
251
checkNotNull (subscription , "subscription" );
252
252
String clusterName = subscription .getClusterName ();
253
253
syncContext .execute (() -> {
254
- XdsWatcherBase <?> cdsWatcher =
255
- resourceWatchers .get (CLUSTER_RESOURCE ).watchers .get (clusterName );
254
+ XdsWatcherBase <?> cdsWatcher = null ;
255
+ if (resourceWatchers .containsKey (CLUSTER_RESOURCE )) {
256
+ cdsWatcher = resourceWatchers .get (CLUSTER_RESOURCE ).watchers .get (clusterName );
257
+ }
256
258
if (cdsWatcher == null ) {
257
259
return ; // already released while waiting for the syncContext
258
260
}
@@ -324,6 +326,13 @@ private void maybePublishConfig() {
324
326
return ;
325
327
}
326
328
329
+ List <String > namesInLoop = detectLoops (rawClusterWatchers );
330
+ if (namesInLoop != null ) {
331
+ String error = "Detected loop in cluster dependencies: " + namesInLoop ;
332
+ xdsConfigWatcher .onError ("xDS node ID: " + dataPlaneAuthority ,
333
+ Status .UNAVAILABLE .withDescription (error ));
334
+ return ;
335
+ }
327
336
XdsConfig newConfig = buildConfig ();
328
337
if (Objects .equals (newConfig , lastXdsConfig )) {
329
338
return ;
@@ -332,6 +341,51 @@ private void maybePublishConfig() {
332
341
xdsConfigWatcher .onUpdate (lastXdsConfig );
333
342
}
334
343
344
+ private List <String > detectLoops (TypeWatchers <?> rawClusterWatchers ) {
345
+ for (XdsWatcherBase <?> watcher : rawClusterWatchers .watchers .values ()) {
346
+ if (!watcher .hasDataValue ()) {
347
+ continue ;
348
+ }
349
+ CdsWatcher cdsWatcher = (CdsWatcher ) watcher ;
350
+
351
+ XdsClusterResource .CdsUpdate cdsUpdate = cdsWatcher .getData ().getValue ();
352
+ if (cdsUpdate .clusterType () != ClusterType .AGGREGATE ) {
353
+ continue ;
354
+ }
355
+ List <String > namesInLoop =
356
+ detectLoops (Arrays .asList (watcher .resourceName ), cdsUpdate .prioritizedClusterNames ());
357
+ if (namesInLoop != null ) {
358
+ return namesInLoop ;
359
+ }
360
+ }
361
+
362
+ return null ;
363
+ }
364
+
365
+ private List <String > detectLoops (List <String > parents , ImmutableList <String > children ) {
366
+ if (!Collections .disjoint (parents , children )) {
367
+ String problemChild = children .stream ().filter (c -> parents .contains (c )).findFirst ().get ();
368
+ return new ImmutableList .Builder <String >().addAll (parents ).add (problemChild ).build ();
369
+ }
370
+
371
+ for (String child : children ) {
372
+ CdsWatcher childWatcher = getCluster (child );
373
+ if (childWatcher == null || !childWatcher .getData ().hasValue ()
374
+ || childWatcher .getData ().getValue ().clusterType () != ClusterType .AGGREGATE ) {
375
+ continue ;
376
+ }
377
+ ImmutableList newParents =
378
+ new ImmutableList .Builder ().addAll (parents ).add (childWatcher .resourceName ()).build ();
379
+ List <String > childLoop =
380
+ detectLoops (newParents , childWatcher .getData ().getValue ().prioritizedClusterNames ());
381
+ if (childLoop != null ) {
382
+ return childLoop ;
383
+ }
384
+ }
385
+
386
+ return null ;
387
+ }
388
+
335
389
@ VisibleForTesting
336
390
XdsConfig buildConfig () {
337
391
XdsConfig .XdsConfigBuilder builder = new XdsConfig .XdsConfigBuilder ();
@@ -988,10 +1042,7 @@ public void onChanged(XdsClusterResource.CdsUpdate update) {
988
1042
setData (update );
989
1043
Set <String > addedClusters = Sets .difference (newNames , oldNames );
990
1044
addedClusters .forEach ((cluster ) -> addClusterWatcher (cluster , parentContext , depth ));
991
-
992
- if (addedClusters .isEmpty ()) {
993
- maybePublishConfig ();
994
- }
1045
+ maybePublishConfig ();
995
1046
} else { // data was set to error status above
996
1047
maybePublishConfig ();
997
1048
}
0 commit comments