private static void updateRecency(BoundedLocalCache<Integer, Integer> cache, CacheContext context, Runnable operation) { Node<Integer, Integer> first = firstBeforeAccess(cache, context); operation.run(); cache.maintenance(/* ignored */ null); if (context.isZeroWeighted()) { assertThat(cache.accessOrderEdenDeque().peekFirst(), is(not(first))); assertThat(cache.accessOrderEdenDeque().peekLast(), is(first)); } else { assertThat(cache.accessOrderProbationDeque().peekFirst(), is(not(first))); assertThat(cache.accessOrderProtectedDeque().peekLast(), is(first)); } }
@Test(dataProvider = "caches") @CacheSpec(compute = Compute.SYNC, implementation = Implementation.Caffeine, population = Population.EMPTY, maximumSize = Maximum.FULL) public void drain_onWrite(Cache<Integer, Integer> cache, CacheContext context) { BoundedLocalCache<Integer, Integer> localCache = asBoundedLocalCache(cache); cache.put(1, 1); int size = localCache.accessOrderEdenDeque().size() + localCache.accessOrderProbationDeque().size(); assertThat(localCache.writeBuffer().size(), is(0)); assertThat(size, is(1)); }
/** 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); } }
/** Promote the node from probation to protected on an access. */ @GuardedBy("evictionLock") void reorderProbation(Node<K, V> node) { if (!accessOrderProbationDeque().contains(node)) { // Ignore stale accesses for an entry that is no longer present return; } else if (node.getPolicyWeight() > mainProtectedMaximum()) { return; } long mainProtectedWeightedSize = mainProtectedWeightedSize() + node.getPolicyWeight(); accessOrderProbationDeque().remove(node); accessOrderProtectedDeque().add(node); node.makeMainProtected(); long mainProtectedMaximum = mainProtectedMaximum(); while (mainProtectedWeightedSize > mainProtectedMaximum) { Node<K, V> demoted = accessOrderProtectedDeque().pollFirst(); if (demoted == null) { break; } demoted.makeMainProbation(); accessOrderProbationDeque().add(demoted); mainProtectedWeightedSize -= demoted.getPolicyWeight(); } lazySetMainProtectedWeightedSize(mainProtectedWeightedSize); }
private static Node<Integer, Integer> firstBeforeAccess( BoundedLocalCache<Integer, Integer> localCache, CacheContext context) { return context.isZeroWeighted() ? localCache.accessOrderEdenDeque().peek() : localCache.accessOrderProbationDeque().peek(); }
/** * Returns an unmodifiable snapshot map ordered in eviction order, either ascending or descending. * Beware that obtaining the mappings is <em>NOT</em> a constant-time operation. * * @param limit the maximum number of entries * @param transformer a function that unwraps the value * @param hottest the iteration order * @return an unmodifiable snapshot in a specified order */ @SuppressWarnings("GuardedByChecker") Map<K, V> evictionOrder(int limit, Function<V, V> transformer, boolean hottest) { Supplier<Iterator<Node<K, V>>> iteratorSupplier = () -> { Comparator<Node<K, V>> comparator = Comparator.comparingInt(node -> { K key = node.getKey(); return (key == null) ? 0 : frequencySketch().frequency(key); }); if (hottest) { PeekingIterator<Node<K, V>> secondary = PeekingIterator.comparing( accessOrderProbationDeque().descendingIterator(), accessOrderEdenDeque().descendingIterator(), comparator); return PeekingIterator.concat(accessOrderProtectedDeque().descendingIterator(), secondary); } else { PeekingIterator<Node<K, V>> primary = PeekingIterator.comparing( accessOrderEdenDeque().iterator(), accessOrderProbationDeque().iterator(), comparator.reversed()); return PeekingIterator.concat(primary, accessOrderProtectedDeque().iterator()); } }; return fixedSnapshot(iteratorSupplier, limit, transformer); }
if (oldest) { first = accessOrderEdenDeque().iterator(); second = accessOrderProbationDeque().iterator(); third = accessOrderProtectedDeque().iterator(); } else { comparator = comparator.reversed(); first = accessOrderEdenDeque().descendingIterator(); second = accessOrderProbationDeque().descendingIterator(); third = accessOrderProtectedDeque().descendingIterator();
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); } }
/** * Evicts entries from the eden space into the main space while the eden size exceeds a maximum. * * @return the number of candidate entries evicted from the eden space */ @GuardedBy("evictionLock") int evictFromEden() { int candidates = 0; Node<K, V> node = accessOrderEdenDeque().peek(); while (edenWeightedSize() > edenMaximum()) { // The pending operations will adjust the size to reflect the correct weight if (node == null) { break; } Node<K, V> next = node.getNextInAccessOrder(); if (node.getWeight() != 0) { node.makeMainProbation(); accessOrderEdenDeque().remove(node); accessOrderProbationDeque().add(node); candidates++; lazySetEdenWeightedSize(edenWeightedSize() - node.getPolicyWeight()); } node = next; } return candidates; }
void evictFromMain(int candidates) { int victimQueue = PROBATION; Node<K, V> victim = accessOrderProbationDeque().peekFirst(); Node<K, V> candidate = accessOrderProbationDeque().peekLast(); while (weightedSize() > maximum()) {
} else if (evicts()) { if (node.inMainProbation()) { accessOrderProbationDeque().remove(node); } else { accessOrderProtectedDeque().remove(node);
} else if (evicts()) { if (node.inMainProbation()) { accessOrderProbationDeque().remove(node); } else { accessOrderProtectedDeque().remove(node);
/** 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); } }
/** Promote the node from probation to protected on an access. */ @GuardedBy("evictionLock") void reorderProbation(Node<K, V> node) { if (!accessOrderProbationDeque().contains(node)) { // Ignore stale accesses for an entry that is no longer present return; } else if (node.getPolicyWeight() > mainProtectedMaximum()) { return; } long mainProtectedWeightedSize = mainProtectedWeightedSize() + node.getPolicyWeight(); accessOrderProbationDeque().remove(node); accessOrderProtectedDeque().add(node); node.makeMainProtected(); long mainProtectedMaximum = mainProtectedMaximum(); while (mainProtectedWeightedSize > mainProtectedMaximum) { Node<K, V> demoted = accessOrderProtectedDeque().pollFirst(); if (demoted == null) { break; } demoted.makeMainProbation(); accessOrderProbationDeque().add(demoted); mainProtectedWeightedSize -= demoted.getPolicyWeight(); } lazySetMainProtectedWeightedSize(mainProtectedWeightedSize); }
/** * Returns an unmodifiable snapshot map ordered in eviction order, either ascending or descending. * Beware that obtaining the mappings is <em>NOT</em> a constant-time operation. * * @param limit the maximum number of entries * @param transformer a function that unwraps the value * @param hottest the iteration order * @return an unmodifiable snapshot in a specified order */ @SuppressWarnings("GuardedByChecker") Map<K, V> evictionOrder(int limit, Function<V, V> transformer, boolean hottest) { Supplier<Iterator<Node<K, V>>> iteratorSupplier = () -> { Comparator<Node<K, V>> comparator = Comparator.comparingInt(node -> { K key = node.getKey(); return (key == null) ? 0 : frequencySketch().frequency(key); }); if (hottest) { PeekingIterator<Node<K, V>> secondary = PeekingIterator.comparing( accessOrderProbationDeque().descendingIterator(), accessOrderEdenDeque().descendingIterator(), comparator); return PeekingIterator.concat(accessOrderProtectedDeque().descendingIterator(), secondary); } else { PeekingIterator<Node<K, V>> primary = PeekingIterator.comparing( accessOrderEdenDeque().iterator(), accessOrderProbationDeque().iterator(), comparator.reversed()); return PeekingIterator.concat(primary, accessOrderProtectedDeque().iterator()); } }; return fixedSnapshot(iteratorSupplier, limit, transformer); }
if (oldest) { first = accessOrderEdenDeque().iterator(); second = accessOrderProbationDeque().iterator(); third = accessOrderProtectedDeque().iterator(); } else { comparator = comparator.reversed(); first = accessOrderEdenDeque().descendingIterator(); second = accessOrderProbationDeque().descendingIterator(); third = accessOrderProtectedDeque().descendingIterator();
/** * Evicts entries from the eden space into the main space while the eden size exceeds a maximum. * * @return the number of candidate entries evicted from the eden space */ @GuardedBy("evictionLock") int evictFromEden() { int candidates = 0; Node<K, V> node = accessOrderEdenDeque().peek(); while (edenWeightedSize() > edenMaximum()) { // The pending operations will adjust the size to reflect the correct weight if (node == null) { break; } Node<K, V> next = node.getNextInAccessOrder(); if (node.getWeight() != 0) { node.makeMainProbation(); accessOrderEdenDeque().remove(node); accessOrderProbationDeque().add(node); candidates++; lazySetEdenWeightedSize(edenWeightedSize() - node.getPolicyWeight()); } node = next; } return candidates; }
void evictFromMain(int candidates) { int victimQueue = PROBATION; Node<K, V> victim = accessOrderProbationDeque().peekFirst(); Node<K, V> candidate = accessOrderProbationDeque().peekLast(); while (weightedSize() > maximum()) {
} else if (evicts()) { if (node.inMainProbation()) { accessOrderProbationDeque().remove(node); } else { accessOrderProtectedDeque().remove(node);
} else if (evicts()) { if (node.inMainProbation()) { accessOrderProbationDeque().remove(node); } else { accessOrderProtectedDeque().remove(node);