Skip to content

Commit

Permalink
Add expireAfterWrite tolerance to handle high write rates
Browse files Browse the repository at this point in the history
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] orbit/orbit#144 (comment)
  • Loading branch information
ben-manes committed Apr 3, 2016
1 parent b011f15 commit 2c5c1d9
Showing 1 changed file with 9 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ abstract class BoundedLocalCache<K, V> extends BLCHeader.DrainStatusRef<K, V>
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<Object, Node<K, V>> data;
final Consumer<Node<K, V>> accessPolicy;
Expand Down Expand Up @@ -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);
}

Expand Down

0 comments on commit 2c5c1d9

Please sign in to comment.