diff options
Diffstat (limited to 'node-repository')
2 files changed, 44 insertions, 7 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabase.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabase.java index 12a0496ed2e..203f52a7b70 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabase.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/persistence/CuratorDatabase.java @@ -38,6 +38,8 @@ public class CuratorDatabase { /** Whether we should return data from the cache or always read fro ZooKeeper */ private final boolean useCache; + private final Object cacheCreationLock = new Object(); + /** * All keys, to allow reentrancy. * This will grow forever with the number of applications seen, but this should be too slow to be a problem. @@ -94,14 +96,42 @@ public class CuratorDatabase { public Optional<byte[]> getData(Path path) { return getCache().getData(path); } + private static class CacheAndGeneration { + public CacheAndGeneration(CuratorDatabaseCache cache, long generation) + { + this.cache = cache; + this.generation = generation; + } + public boolean expired() { + return generation != cache.generation(); + } + public CuratorDatabaseCache validCache() { + if (expired()) { + throw new IllegalStateException("The cache has generation " + cache.generation() + + " while the root genration counter in zookeeper says " + generation + + ". That is totally unacceptable and must be a sever programming error in my close vicinity."); + } + return cache; + } + + private CuratorDatabaseCache cache; + private long generation; + } + private CacheAndGeneration getCacheSnapshot() { + return new CacheAndGeneration(cache.get(), changeGenerationCounter.get()); + } private CuratorDatabaseCache getCache() { - CuratorDatabaseCache cache = this.cache.get(); - long currentCuratorGeneration = changeGenerationCounter.get(); - if (currentCuratorGeneration != cache.generation()) { // current cache is invalid - start new - cache = newCache(currentCuratorGeneration); - this.cache.set(cache); + CacheAndGeneration cacheAndGeneration = getCacheSnapshot(); + while (cacheAndGeneration.expired()) { + synchronized (cacheCreationLock) { // Prevent a race for creating new caches + cacheAndGeneration = getCacheSnapshot(); + if (cacheAndGeneration.expired()) { + cache.set(newCache(changeGenerationCounter.get())); + cacheAndGeneration = getCacheSnapshot(); + } + } } - return cache; + return cacheAndGeneration.validCache(); } /** Caches must only be instantiated using this method */ diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifier.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifier.java index 6d92f9c4541..bea7973541a 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifier.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodeIdentifier.java @@ -1,6 +1,8 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.provision.restapi.v2.filter; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Zone; import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; @@ -11,6 +13,7 @@ import com.yahoo.vespa.hosted.provision.NodeRepository; import java.security.cert.X509Certificate; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static com.yahoo.vespa.athenz.tls.SubjectAlternativeName.Type.DNS_NAME; @@ -31,9 +34,13 @@ class NodeIdentifier { private final Zone zone; private final NodeRepository nodeRepository; + + private final Supplier<List<Node>> nodeCache; + NodeIdentifier(Zone zone, NodeRepository nodeRepository) { this.zone = zone; this.nodeRepository = nodeRepository; + nodeCache = Suppliers.memoizeWithExpiration(nodeRepository::getNodes, 1, TimeUnit.MINUTES); } NodePrincipal resolveNode(List<X509Certificate> certificateChain) throws NodeIdentifierException { @@ -73,7 +80,7 @@ class NodeIdentifier { private String getHostFromCalypsoCertificate(List<SubjectAlternativeName> sans) { String openstackId = getUniqueInstanceId(sans); - return nodeRepository.getNodes().stream() + return nodeCache.get().stream() .filter(node -> node.openStackId().equals(openstackId)) .map(Node::hostname) .findFirst() |