From de376c730c93fd78f75bd8ec863e6dd7ce6b355f Mon Sep 17 00:00:00 2001 From: Henning Baldersheim Date: Wed, 1 Sep 2021 13:47:37 +0200 Subject: Ensure that only one thread will compute the missing object in the cache. The others will wait. --- .../com/yahoo/vespa/config/server/ServerCache.java | 27 +++++++++++++++++++--- .../config/server/application/Application.java | 11 ++++----- .../yahoo/vespa/config/server/ServerCacheTest.java | 6 ++--- 3 files changed, 31 insertions(+), 13 deletions(-) (limited to 'configserver') diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ServerCache.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ServerCache.java index ad544005ace..85bf5df3c71 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/ServerCache.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ServerCache.java @@ -9,6 +9,7 @@ import com.yahoo.vespa.config.protocol.ConfigResponse; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; /** * Cache that holds configs and config definitions (builtin and user config definitions). @@ -23,11 +24,14 @@ public class ServerCache { // NOTE: The reason we do a double mapping here is to de-dupe configs that have the same md5. private final Map md5Sums = new ConcurrentHashMap<>(); private final Map md5ToConfig = new ConcurrentHashMap<>(); - + private final Object [] stripedLocks = new Object[113]; public ServerCache(ConfigDefinitionRepo builtinConfigDefinitions, ConfigDefinitionRepo userConfigDefinitions) { this.builtinConfigDefinitions = builtinConfigDefinitions; this.userConfigDefinitions = userConfigDefinitions; + for (int i = 0; i < stripedLocks.length; i++) { + stripedLocks[i] = new Object(); + } } // For testing only @@ -35,17 +39,34 @@ public class ServerCache { this(new StaticConfigDefinitionRepo(), new UserConfigDefinitionRepo()); } - public void put(ConfigCacheKey key, ConfigResponse config, String configMd5) { + private void put(ConfigCacheKey key, ConfigResponse config) { + String configMd5 = config.getConfigMd5(); md5Sums.put(key, configMd5); md5ToConfig.put(configMd5, config); } - public ConfigResponse get(ConfigCacheKey key) { + ConfigResponse get(ConfigCacheKey key) { String md5 = md5Sums.get(key); if (md5 == null) return null; return md5ToConfig.get(md5); } + public ConfigResponse computeIfAbsent(ConfigCacheKey key, Function mappingFunction) { + ConfigResponse config = get(key); + if (config != null) { + return config; + } + synchronized (stripedLocks[Math.abs(key.hashCode()%stripedLocks.length)]) { + String md5 = md5Sums.get(key); + if (md5 == null) { + config = mappingFunction.apply(key); + put(key, config); + return config; + } + return md5ToConfig.get(md5); + } + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java b/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java index 2133850e674..0afb3049f6e 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/application/Application.java @@ -111,15 +111,12 @@ public class Application implements ModelResult { ConfigResponse config; if (useCache(req)) { - config = cache.get(cacheKey); - if (config != null) { - log.log(Level.FINE, () -> TenantRepository.logPre(getId()) + ("Found config " + cacheKey + " in cache")); - } else { - config = createConfigResponse(configKey, req, responseFactory); - cache.put(cacheKey, config, config.getConfigMd5()); + config = cache.computeIfAbsent(cacheKey, (ConfigCacheKey key) -> { + var response = createConfigResponse(configKey, req, responseFactory); metricUpdater.setCacheConfigElems(cache.configElems()); metricUpdater.setCacheChecksumElems(cache.checkSumElems()); - } + return response; + }); } else { config = createConfigResponse(configKey, req, responseFactory); } diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/ServerCacheTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/ServerCacheTest.java index 35449238f78..9fca80b7a11 100644 --- a/configserver/src/test/java/com/yahoo/vespa/config/server/ServerCacheTest.java +++ b/configserver/src/test/java/com/yahoo/vespa/config/server/ServerCacheTest.java @@ -48,9 +48,9 @@ public class ServerCacheTest { cache = new ServerCache(new TestConfigDefinitionRepo(), userConfigDefinitionRepo); - cache.put(fooBarCacheKey, SlimeConfigResponse.fromConfigPayload(ConfigPayload.empty(), 2, false, configMd5), configMd5); - cache.put(bazQuuxCacheKey, SlimeConfigResponse.fromConfigPayload(ConfigPayload.empty(), 2, false, configMd5), configMd5); - cache.put(fooBarCacheKeyDifferentMd5, SlimeConfigResponse.fromConfigPayload(ConfigPayload.empty(), 2, false, configMd5_2), configMd5_2); + cache.computeIfAbsent(fooBarCacheKey, (ConfigCacheKey key) -> SlimeConfigResponse.fromConfigPayload(ConfigPayload.empty(), 2, false, configMd5)); + cache.computeIfAbsent(bazQuuxCacheKey, (ConfigCacheKey key) -> SlimeConfigResponse.fromConfigPayload(ConfigPayload.empty(), 2, false, configMd5)); + cache.computeIfAbsent(fooBarCacheKeyDifferentMd5, (ConfigCacheKey key) -> SlimeConfigResponse.fromConfigPayload(ConfigPayload.empty(), 2, false, configMd5_2)); } @Test -- cgit v1.2.3