Skip to content

Commit ea7176b

Browse files
TQJADEclaude
andauthored
parallelize InformerManager changeNamespaces (#3430)
* parallelize InformerManager changeNamespaces InformerManager#changeNamespaces starts informers for newly added namespaces sequentially via Set#forEach. Each source.start() call blocks up to cacheSyncTimeout waiting for cache sync, so when N namespaces are added and any of them are slow or RBAC-denied (a common case under stopOnInformerErrorDuringStartup=false), the total wall-clock is O(N * cacheSyncTimeout). The Controller holds its EventProcessor stopped across this entire call, dropping watch events with "Skipping event ... because the event processor is not started" and producing multi-minute reconcile delays after dynamic namespace updates. Align changeNamespaces with the existing parallel pattern already used by InformerManager#start (boundedExecuteAndWaitForAllToComplete on the caching executor). Wall-clock drops from O(N * timeout) to O(timeout). The sources map is already a ConcurrentHashMap, so no additional synchronization is required. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Qi Tan <16416018+TQJADE@users.noreply.github.com>
1 parent 0a90abc commit ea7176b

1 file changed

Lines changed: 18 additions & 8 deletions

File tree

  • operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,24 @@ public void changeNamespaces(Set<String> namespaces) {
113113
log.debug("Stopped informer {} for namespaces: {}", this, sourcesToRemove);
114114
sourcesToRemove.forEach(k -> sources.remove(k).stop());
115115

116-
namespaces.forEach(
117-
ns -> {
118-
if (!sources.containsKey(ns)) {
119-
final InformerWrapper<R> source = createEventSourceForNamespace(ns);
120-
source.start();
121-
log.debug("Registered new {} -> {} for namespace: {}", this, source, ns);
122-
}
123-
});
116+
var newNamespaces =
117+
namespaces.stream().filter(ns -> !sources.containsKey(ns)).collect(Collectors.toList());
118+
if (newNamespaces.isEmpty()) {
119+
return;
120+
}
121+
122+
controllerConfiguration
123+
.getConfigurationService()
124+
.getExecutorServiceManager()
125+
.boundedExecuteAndWaitForAllToComplete(
126+
newNamespaces.stream(),
127+
ns -> {
128+
final var source = createEventSourceForNamespace(ns);
129+
source.start();
130+
log.debug("Registered new {} -> {} for namespace: {}", this, source, ns);
131+
return null;
132+
},
133+
ns -> "InformerStarter-" + ns + "-" + configuration.getResourceClass().getSimpleName());
124134
}
125135

126136
private InformerWrapper<R> createEventSourceForNamespace(String namespace) {

0 commit comments

Comments
 (0)