From 2c5c1d96106cebb8d036043ffdd897f053fd32bb Mon Sep 17 00:00:00 2001 From: Ben Manes Date: Sat, 2 Apr 2016 18:30:26 -0700 Subject: [PATCH] Add expireAfterWrite tolerance to handle high write rates Full details are described in [1]. In a JMH write benchmark, the write buffer was overwelmed and caused degredations. This only occurs for expireAfterWrite due to needing to reorder the entry on the O(1) queue after an update. Due to the high rate of updates and an unbounded buffer, the system would perform poorly. Similar behavior was only seen for maximumSize stress testing of inserts. In both cases a Thread.yield resolves the problem. This case is more likely to be seen by users benchmarking their apps, so it is unfair to not resolve it. An update within a short time window (1 second) is not reordered on the expireAfterWrite queue. This allows the async maintenance to keep up without a problem, while honoring the user's expectations. [1] https://github.com/orbit/orbit/pull/144#issuecomment-204838214 --- .../benmanes/caffeine/cache/BoundedLocalCache.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java index 38646c0689..b823ca6bf9 100644 --- a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java +++ b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java @@ -131,6 +131,8 @@ abstract class BoundedLocalCache extends BLCHeader.DrainStatusRef static final double PERCENT_MAIN = 0.99f; /** The percent of the maximum weighted capacity dedicated to the main's protected space. */ static final double PERCENT_MAIN_PROTECTED = 0.80f; + /** The maximum time window between updates before the expiration time must be recorded. */ + static final long EXPIRE_WRITE_TOLERANCE = TimeUnit.SECONDS.toNanos(1); final ConcurrentHashMap> data; final Consumer> accessPolicy; @@ -1454,10 +1456,15 @@ V putFast(K key, V value, int newWeight, boolean notifyWriter, boolean onlyIfAbs } int weightedDifference = mayUpdate ? (newWeight - oldWeight) : 0; - if ((oldValue == null) || (weightedDifference != 0) || expired - || (!onlyIfAbsent && (oldValue != null) && expiresAfterWrite())) { + if ((oldValue == null) || (weightedDifference != 0) || expired) { + afterWrite(prior, new UpdateTask(prior, weightedDifference), now); + } else if (!onlyIfAbsent && (oldValue != null) && expiresAfterWrite() + && ((now - prior.getWriteTime()) > EXPIRE_WRITE_TOLERANCE)) { afterWrite(prior, new UpdateTask(prior, weightedDifference), now); } else { + if (!onlyIfAbsent) { + prior.setWriteTime(now); + } afterRead(prior, now, false); }