Skip to content

Commit b609abe

Browse files
fix: update KubernetesFromConfigMapProcessor for Caffeine 3.x compatibility
Replace async refresh() with synchronous put() and explicit invalidate() for keys removed from ConfigMap. This fixes a race condition introduced in Caffeine 3.x where asMap().get() returns null for expired entries during an in-flight async refresh, causing KubernetesFromConfigMapTest to fail intermittently. Changes: - Replace LoadingCache with Cache (CacheLoader no longer needed) - Remove expireAfterWrite (entries managed explicitly by scheduler) - Use put() to synchronously write ConfigMap data into cache - Use invalidate() to remove keys no longer present in ConfigMap - Remove ConfigMapGetterCacheLoader inner class Agent-Logs-Url: https://github.com/kubernetes-client/java/sessions/07e0048a-55b8-42c1-809c-ff20e3bd4837 Co-authored-by: brendandburns <5751682+brendandburns@users.noreply.github.com>
1 parent cac82df commit b609abe

1 file changed

Lines changed: 10 additions & 34 deletions

File tree

spring/src/main/java/io/kubernetes/client/spring/extended/manifests/KubernetesFromConfigMapProcessor.java

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212
*/
1313
package io.kubernetes.client.spring.extended.manifests;
1414

15-
import com.github.benmanes.caffeine.cache.CacheLoader;
15+
import com.github.benmanes.caffeine.cache.Cache;
1616
import com.github.benmanes.caffeine.cache.Caffeine;
17-
import com.github.benmanes.caffeine.cache.LoadingCache;
1817
import io.kubernetes.client.openapi.models.V1ConfigMap;
1918
import io.kubernetes.client.spring.extended.manifests.annotation.FromConfigMap;
2019
import io.kubernetes.client.spring.extended.manifests.config.KubernetesManifestsProperties;
@@ -24,9 +23,6 @@
2423
import java.util.concurrent.Executors;
2524
import java.util.concurrent.ScheduledExecutorService;
2625
import java.util.concurrent.TimeUnit;
27-
import java.util.function.Supplier;
28-
import org.checkerframework.checker.nullness.qual.NonNull;
29-
import org.checkerframework.checker.nullness.qual.Nullable;
3026
import org.slf4j.Logger;
3127
import org.slf4j.LoggerFactory;
3228
import org.springframework.beans.BeansException;
@@ -80,15 +76,7 @@ public Object postProcessAfterInitialization(Object bean, String beanName) throw
8076
ConfigMapGetter configMapGetter =
8177
getOrCreateConfigMapGetter(fromConfigMapAnnotation, applicationContext);
8278

83-
LoadingCache<String, String> configMapDataCache =
84-
Caffeine.newBuilder()
85-
.expireAfterWrite(manifestsProperties.getRefreshInterval())
86-
.build(
87-
new ConfigMapGetterCacheLoader(
88-
() -> {
89-
return configMapGetter.get(
90-
fromConfigMapAnnotation.namespace(), fromConfigMapAnnotation.name());
91-
}));
79+
Cache<String, String> configMapDataCache = Caffeine.newBuilder().build();
9280
fullyRefreshCache(configMapGetter, fromConfigMapAnnotation, configMapDataCache);
9381
configMapKeyRefresher.scheduleAtFixedRate(
9482
() -> {
@@ -106,14 +94,20 @@ public Object postProcessAfterInitialization(Object bean, String beanName) throw
10694
private static void fullyRefreshCache(
10795
ConfigMapGetter configMapGetter,
10896
FromConfigMap fromConfigMapAnnotation,
109-
LoadingCache<String, String> configMapDataCache) {
97+
Cache<String, String> configMapDataCache) {
11098
V1ConfigMap configMap =
11199
configMapGetter.get(fromConfigMapAnnotation.namespace(), fromConfigMapAnnotation.name());
112100
if (configMap == null || configMap.getData() == null) {
101+
configMapDataCache.invalidateAll();
113102
return;
114103
}
115104
// TODO: make the cache data refreshment atomic
116-
configMap.getData().keySet().stream().forEach(key -> configMapDataCache.refresh(key));
105+
Map<String, String> newData = configMap.getData();
106+
newData.forEach(configMapDataCache::put);
107+
configMapDataCache.asMap().keySet().stream()
108+
.filter(key -> !newData.containsKey(key))
109+
.collect(java.util.stream.Collectors.toList())
110+
.forEach(configMapDataCache::invalidate);
117111
}
118112

119113
private ConfigMapGetter getOrCreateConfigMapGetter(
@@ -144,22 +138,4 @@ private ConfigMapGetter getOrCreateConfigMapGetter(
144138
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
145139
this.applicationContext = applicationContext;
146140
}
147-
148-
static class ConfigMapGetterCacheLoader implements CacheLoader<String, String> {
149-
150-
ConfigMapGetterCacheLoader(Supplier<V1ConfigMap> configMapSupplier) {
151-
this.configMapSupplier = configMapSupplier;
152-
}
153-
154-
private final Supplier<V1ConfigMap> configMapSupplier;
155-
156-
@Override
157-
public @Nullable String load(@NonNull String key) throws Exception {
158-
V1ConfigMap configMap = this.configMapSupplier.get();
159-
if (configMap == null || configMap.getData() == null) {
160-
return null;
161-
}
162-
return configMap.getData().get(key);
163-
}
164-
}
165141
}

0 commit comments

Comments
 (0)