/** Drains the write buffer. */ @GuardedBy("evictionLock") void drainWriteBuffer() { if (!buffersWrites()) { return; } for (int i = 0; i < WRITE_BUFFER_MAX; i++) { Runnable task = writeBuffer().poll(); if (task == null) { break; } task.run(); } }
@Override @SuppressWarnings("FutureReturnValueIgnored") public void clear() { evictionLock.lock(); try { long now = expirationTicker().read(); // Apply all pending writes Runnable task; while (buffersWrites() && (task = writeBuffer().poll()) != null) { task.run(); } // Discard all entries for (Node<K, V> node : data.values()) { removeNode(node, now); } // Discard all pending reads readBuffer.drainTo(e -> {}); } finally { evictionLock.unlock(); } }
@Test(dataProvider = "caches") @CacheSpec(compute = Compute.SYNC, implementation = Implementation.Caffeine, population = Population.EMPTY, maximumSize = Maximum.FULL) public void exceedsMaximumBufferSize_onWrite(Cache<Integer, Integer> cache, CacheContext context) { BoundedLocalCache<Integer, Integer> localCache = asBoundedLocalCache(cache); boolean[] ran = new boolean[1]; localCache.afterWrite(() -> ran[0] = true); assertThat(ran[0], is(true)); assertThat(localCache.writeBuffer().size(), is(0)); }
@Test(dataProvider = "caches") @CacheSpec(compute = Compute.SYNC, implementation = Implementation.Caffeine, population = Population.FULL, maximumSize = Maximum.FULL) public void afterWrite_drainFullWriteBuffer(Cache<Integer, Integer> cache, CacheContext context) { BoundedLocalCache<Integer, Integer> localCache = asBoundedLocalCache(cache); localCache.drainStatus = PROCESSING_TO_IDLE; int[] processed = { 0 }; Runnable pendingTask = () -> processed[0]++; int[] expectedCount = { 0 }; while (localCache.writeBuffer().offer(pendingTask)) { expectedCount[0]++; } int[] triggered = { 0 }; Runnable triggerTask = () -> triggered[0] = 1 + expectedCount[0]; localCache.afterWrite(triggerTask); assertThat(processed[0], is(expectedCount[0])); assertThat(triggered[0], is(expectedCount[0] + 1)); }
/** * Performs the post-processing work required after a write. * * @param task the pending operation to be applied */ void afterWrite(Runnable task) { if (buffersWrites()) { for (int i = 0; i < WRITE_BUFFER_RETRIES; i++) { if (writeBuffer().offer(task)) { scheduleAfterWrite(); return; } scheduleDrainBuffers(); } // The maintenance task may be scheduled but not running due to all of the executor's threads // being busy. If all of the threads are writing into the cache then no progress can be made // without assistance. try { performCleanUp(task); } catch (RuntimeException e) { logger.log(Level.SEVERE, "Exception thrown when performing the maintenance task", e); } } else { scheduleAfterWrite(); } }
private void drain(BoundedLocalCache<K, V> cache) { do { cache.cleanUp(); } while (cache.buffersWrites() && cache.writeBuffer().size() > 0); }
private void status() { int drainStatus; int pendingWrites; local.evictionLock.lock(); try { pendingWrites = local.writeBuffer().size(); drainStatus = local.drainStatus(); } finally { local.evictionLock.unlock(); } LocalTime elapsedTime = LocalTime.ofSecondOfDay(stopwatch.elapsed(TimeUnit.SECONDS)); System.out.printf("---------- %s ----------%n", elapsedTime); System.out.printf("Pending reads: %,d; writes: %,d%n", local.readBuffer.size(), pendingWrites); System.out.printf("Drain status = %s (%s)%n", STATUS[drainStatus], drainStatus); System.out.printf("Evictions = %,d%n", cache.stats().evictionCount()); System.out.printf("Size = %,d (max: %,d)%n", local.data.mappingCount(), operation.maxEntries); System.out.printf("Lock = [%s%n", StringUtils.substringAfter( local.evictionLock.toString(), "[")); System.out.printf("Pending tasks = %,d%n", ForkJoinPool.commonPool().getQueuedSubmissionCount()); long maxMemory = Runtime.getRuntime().maxMemory(); long freeMemory = Runtime.getRuntime().freeMemory(); long allocatedMemory = Runtime.getRuntime().totalMemory(); System.out.printf("Max Memory = %,d bytes%n", maxMemory); System.out.printf("Free Memory = %,d bytes%n", freeMemory); System.out.printf("Allocated Memory = %,d bytes%n", allocatedMemory); System.out.println(); }
@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)); }
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)); } }
/** Drains the write buffer. */ @GuardedBy("evictionLock") void drainWriteBuffer() { if (!buffersWrites()) { return; } for (int i = 0; i < WRITE_BUFFER_MAX; i++) { Runnable task = writeBuffer().poll(); if (task == null) { break; } task.run(); } }
@Override @SuppressWarnings("FutureReturnValueIgnored") public void clear() { evictionLock.lock(); try { long now = expirationTicker().read(); // Apply all pending writes Runnable task; while (buffersWrites() && (task = writeBuffer().poll()) != null) { task.run(); } // Discard all entries for (Node<K, V> node : data.values()) { removeNode(node, now); } // Discard all pending reads readBuffer.drainTo(e -> {}); } finally { evictionLock.unlock(); } }
/** * Performs the post-processing work required after a write. * * @param task the pending operation to be applied */ void afterWrite(Runnable task) { if (buffersWrites()) { for (int i = 0; i < WRITE_BUFFER_RETRIES; i++) { if (writeBuffer().offer(task)) { scheduleAfterWrite(); return; } scheduleDrainBuffers(); } // The maintenance task may be scheduled but not running due to all of the executor's threads // being busy. If all of the threads are writing into the cache then no progress can be made // without assistance. try { performCleanUp(task); } catch (RuntimeException e) { logger.log(Level.SEVERE, "Exception thrown when performing the maintenance task", e); } } else { scheduleAfterWrite(); } }