@Override public Optional<Eviction<K, V>> eviction() { return cache.evicts() ? (eviction == null) ? (eviction = Optional.of(new BoundedEviction())) : eviction : Optional.empty(); } @Override public Optional<Expiration<K, V>> expireAfterAccess() {
/** Evicts entries if the cache exceeds the maximum. */ @GuardedBy("evictionLock") void evictEntries() { if (!evicts()) { return; } int candidates = evictFromEden(); evictFromMain(candidates); }
/** Creates an instance based on the builder's configuration. */ protected BoundedLocalCache(Caffeine<K, V> builder, @Nullable CacheLoader<K, V> cacheLoader, boolean isAsync) { this.isAsync = isAsync; this.cacheLoader = cacheLoader; executor = builder.getExecutor(); writer = builder.getCacheWriter(); evictionLock = new ReentrantLock(); weigher = builder.getWeigher(isAsync); drainBuffersTask = new PerformCleanupTask(); nodeFactory = NodeFactory.newFactory(builder, isAsync); data = new ConcurrentHashMap<>(builder.getInitialCapacity()); readBuffer = evicts() || collectKeys() || collectValues() || expiresAfterAccess() ? new BoundedBuffer<>() : Buffer.disabled(); accessPolicy = (evicts() || expiresAfterAccess()) ? this::onAccess : e -> {}; if (evicts()) { setMaximum(builder.getMaximum()); } }
/** Expires entries in the access-order queue. */ @GuardedBy("evictionLock") void expireAfterAccessEntries(long now) { if (!expiresAfterAccess()) { return; } expireAfterAccessEntries(accessOrderEdenDeque(), now); if (evicts()) { expireAfterAccessEntries(accessOrderProbationDeque(), now); expireAfterAccessEntries(accessOrderProtectedDeque(), now); } }
/** Force the random seed to a predictable value. */ public static void ensureRandomSeed(Cache<?, ?> cache) { BoundedLocalCache<?, ?> map = unwrap(cache); if (map == null) { return; } resetThreadLocalRandom(); if (map.evicts()) { ensureRandomSeed(map.frequencySketch()); } }
private void checkCache(BoundedLocalCache<K, V> cache) { try { if (cache.evictionLock.tryLock(5, TimeUnit.SECONDS)) { cache.evictionLock.unlock(); } else { desc.expected("Maintenance lock can be acquired"); } } catch (InterruptedException e) { desc.expected("Maintenance lock can be acquired: " + Throwables.getStackTraceAsString(e)); } desc.expectThat("Inconsistent size", cache.data.size(), is(cache.size())); if (cache.evicts()) { cache.evictionLock.lock(); try { long weightedSize = cache.weightedSize(); desc.expectThat("overflow", cache.maximum(), is(greaterThanOrEqualTo(weightedSize))); } finally { cache.evictionLock.unlock(); } } if (cache.isEmpty()) { desc.expectThat("empty map", cache, emptyMap()); } for (Node<K, V> node : cache.data.values()) { checkNode(cache, node, desc); } }
private void checkLinks(BoundedLocalCache<K, V> cache, ImmutableList<LinkedDeque<Node<K, V>>> deques, DescriptionBuilder desc) { int size = 0; long weightedSize = 0; Set<Node<K, V>> seen = Sets.newIdentityHashSet(); for (LinkedDeque<Node<K, V>> deque : deques) { size += deque.size(); weightedSize += scanLinks(cache, seen, deque, desc); } if (cache.size() != size) { desc.expectThat(() -> "deque size " + deques, size, is(cache.size())); } Supplier<String> errorMsg = () -> String.format( "Size != list length; pending=%s, additional: %s", cache.writeBuffer().size(), Sets.difference(seen, ImmutableSet.copyOf(cache.data.values()))); desc.expectThat(errorMsg, cache.size(), is(seen.size())); final long weighted = weightedSize; if (cache.evicts()) { Supplier<String> error = () -> String.format( "WeightedSize != link weights [%d vs %d] {%d vs %d}", cache.adjustedWeightedSize(), weighted, seen.size(), cache.size()); desc.expectThat("non-negative weight", weightedSize, is(greaterThanOrEqualTo(0L))); desc.expectThat(error, cache.adjustedWeightedSize(), is(weightedSize)); } }
desc.expectThat("same nodeFactory", copy.nodeFactory, instanceOf(original.nodeFactory.getClass())); if (original.evicts()) { desc.expectThat("same maximumWeight", copy.maximum(), is(original.maximum())); desc.expectThat("same maximumEdenWeight", copy.edenMaximum(), is(original.edenMaximum()));
if (!evicts()) { Supplier<Iterator<Node<K, V>>> iteratorSupplier = () -> oldest ? accessOrderEdenDeque().iterator()
private void checkEvictionDeque(BoundedLocalCache<K, V> cache) { if (cache.evicts()) { ImmutableList<LinkedDeque<Node<K, V>>> deques = ImmutableList.of( cache.accessOrderEdenDeque(), cache.accessOrderProbationDeque(), cache.accessOrderProtectedDeque()); checkLinks(cache, deques, desc); checkDeque(cache.accessOrderEdenDeque(), desc); checkDeque(cache.accessOrderProbationDeque(), desc); } else if (cache.expiresAfterAccess()) { checkLinks(cache, ImmutableList.of(cache.accessOrderEdenDeque()), desc); checkDeque(cache.accessOrderEdenDeque(), desc); } if (cache.expiresAfterWrite()) { checkLinks(cache, ImmutableList.of(cache.writeOrderDeque()), desc); checkDeque(cache.writeOrderDeque(), desc); } }
/** * Atomically transitions the node to the <tt>dead</tt> state and decrements the * <tt>weightedSize</tt>. * * @param node the entry in the page replacement policy */ @GuardedBy("evictionLock") void makeDead(Node<K, V> node) { synchronized (node) { if (node.isDead()) { return; } if (evicts()) { // The node's policy weight may be out of sync due to a pending update waiting to be // processed. At this point the node's weight is finalized, so the weight can be safely // taken from the node's perspective and the sizes will be adjusted correctly. if (node.inEden()) { lazySetEdenWeightedSize(edenWeightedSize() - node.getWeight()); } else if (node.inMainProtected()) { lazySetMainProtectedWeightedSize(mainProtectedWeightedSize() - node.getWeight()); } lazySetWeightedSize(weightedSize() - node.getWeight()); } node.die(); } }
/** Creates a serialization proxy based on the common configuration shared by all cache types. */ static <K, V> SerializationProxy<K, V> makeSerializationProxy( BoundedLocalCache<?, ?> cache, boolean isWeighted) { SerializationProxy<K, V> proxy = new SerializationProxy<>(); proxy.weakKeys = cache.collectKeys(); proxy.weakValues = cache.nodeFactory.weakValues(); proxy.softValues = cache.nodeFactory.softValues(); proxy.isRecordingStats = cache.isRecordingStats(); proxy.removalListener = cache.removalListener(); proxy.ticker = cache.expirationTicker(); proxy.writer = cache.writer; if (cache.expiresAfterAccess()) { proxy.expiresAfterAccessNanos = cache.expiresAfterAccessNanos(); } if (cache.expiresAfterWrite()) { proxy.expiresAfterWriteNanos = cache.expiresAfterWriteNanos(); } if (cache.expiresVariable()) { proxy.expiry = cache.expiry(); } if (cache.evicts()) { if (isWeighted) { proxy.weigher = cache.weigher; proxy.maximumWeight = cache.maximum(); } else { proxy.maximumSize = cache.maximum(); } } return proxy; }
/** Updates the node's location in the page replacement policy. */ @GuardedBy("evictionLock") void onAccess(Node<K, V> node) { if (evicts()) { K key = node.getKey(); if (key == null) { return; } frequencySketch().increment(key); if (node.inEden()) { reorder(accessOrderEdenDeque(), node); } else if (node.inMainProbation()) { reorderProbation(node); } else { reorder(accessOrderProtectedDeque(), node); } } else if (expiresAfterAccess()) { reorder(accessOrderEdenDeque(), node); } if (expiresVariable()) { timerWheel().reschedule(node); } }
@Override public Optional<Eviction<K, V>> eviction() { return cache.evicts() ? (eviction == null) ? (eviction = Optional.of(new BoundedEviction())) : eviction : Optional.empty(); } @Override public Optional<Expiration<K, V>> expireAfterAccess() {
/** Evicts entries if the cache exceeds the maximum. */ @GuardedBy("evictionLock") void evictEntries() { if (!evicts()) { return; } int candidates = evictFromEden(); evictFromMain(candidates); }
/** Creates an instance based on the builder's configuration. */ protected BoundedLocalCache(Caffeine<K, V> builder, @Nullable CacheLoader<K, V> cacheLoader, boolean isAsync) { this.isAsync = isAsync; this.cacheLoader = cacheLoader; executor = builder.getExecutor(); writer = builder.getCacheWriter(); evictionLock = new ReentrantLock(); weigher = builder.getWeigher(isAsync); drainBuffersTask = new PerformCleanupTask(); nodeFactory = NodeFactory.newFactory(builder, isAsync); data = new ConcurrentHashMap<>(builder.getInitialCapacity()); readBuffer = evicts() || collectKeys() || collectValues() || expiresAfterAccess() ? new BoundedBuffer<>() : Buffer.disabled(); accessPolicy = (evicts() || expiresAfterAccess()) ? this::onAccess : e -> {}; if (evicts()) { setMaximum(builder.getMaximum()); } }
/** Expires entries in the access-order queue. */ @GuardedBy("evictionLock") void expireAfterAccessEntries(long now) { if (!expiresAfterAccess()) { return; } expireAfterAccessEntries(accessOrderEdenDeque(), now); if (evicts()) { expireAfterAccessEntries(accessOrderProbationDeque(), now); expireAfterAccessEntries(accessOrderProtectedDeque(), now); } }
/** Updates the node's location in the page replacement policy. */ @GuardedBy("evictionLock") void onAccess(Node<K, V> node) { if (evicts()) { K key = node.getKey(); if (key == null) { return; } frequencySketch().increment(key); if (node.inEden()) { reorder(accessOrderEdenDeque(), node); } else if (node.inMainProbation()) { reorderProbation(node); } else { reorder(accessOrderProtectedDeque(), node); } } else if (expiresAfterAccess()) { reorder(accessOrderEdenDeque(), node); } if (expiresVariable()) { timerWheel().reschedule(node); } }