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 e4c07c5458..d25d3049b3 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 @@ -1523,6 +1523,20 @@ void setAccessTime(Node node, long now) { } } + /** Returns if the entry's write time would exceed the minimum expiration reorder threshold. */ + boolean exceedsWriteTimeTolerance(Node node, long varTime, long now) { + boolean exceedsTolerance; + exceedsTolerance = (expiresAfterWrite() + && (expiresAfterWriteNanos() <= EXPIRE_WRITE_TOLERANCE) + || (Math.abs(now - node.getWriteTime()) > EXPIRE_WRITE_TOLERANCE)); + exceedsTolerance |= (refreshAfterWrite() + && (refreshAfterWriteNanos() <= EXPIRE_WRITE_TOLERANCE) + || (Math.abs(now - node.getWriteTime()) > EXPIRE_WRITE_TOLERANCE)); + exceedsTolerance |= (expiresVariable() + && (Math.abs(varTime - node.getVariableTime()) > EXPIRE_WRITE_TOLERANCE)); + return exceedsTolerance; + } + /** * Performs the post-processing work required after a write. * @@ -2298,8 +2312,8 @@ public void putAll(Map map) { if (node == null) { node = nodeFactory.newNode(key, keyReferenceQueue(), value, valueReferenceQueue(), newWeight, now); - setVariableTime(node, expireAfterCreate(key, value, expiry, now)); long expirationTime = isComputingAsync(value) ? (now + ASYNC_EXPIRY) : now; + setVariableTime(node, expireAfterCreate(key, value, expiry, now)); setAccessTime(node, expirationTime); setWriteTime(node, expirationTime); } @@ -2384,11 +2398,10 @@ public void putAll(Map map) { long expirationTime = isComputingAsync(value) ? (now + ASYNC_EXPIRY) : now; if (mayUpdate) { - exceedsTolerance = - (expiresAfterWrite() && (now - prior.getWriteTime()) > EXPIRE_WRITE_TOLERANCE) - || (expiresVariable() - && Math.abs(varTime - prior.getVariableTime()) > EXPIRE_WRITE_TOLERANCE); - setWriteTime(prior, expirationTime); + exceedsTolerance = exceedsWriteTimeTolerance(prior, varTime, now); + if (expired || exceedsTolerance) { + setWriteTime(prior, isComputingAsync(value) ? (now + ASYNC_EXPIRY) : now); + } prior.setValue(value, valueReferenceQueue()); prior.setWeight(newWeight); @@ -2514,8 +2527,9 @@ public boolean remove(Object key, Object value) { requireNonNull(key); requireNonNull(value); - long[] now = new long[1]; + var now = new long[1]; var oldWeight = new int[1]; + var exceedsTolerance = new boolean[1]; @SuppressWarnings({"unchecked", "Varifier"}) @Nullable K[] nodeKey = (K[]) new Object[1]; @SuppressWarnings({"unchecked", "Varifier"}) @@ -2538,8 +2552,11 @@ public boolean remove(Object key, Object value) { n.setWeight(weight); long expirationTime = isComputingAsync(value) ? (now[0] + ASYNC_EXPIRY) : now[0]; + exceedsTolerance[0] = exceedsWriteTimeTolerance(n, varTime, expirationTime); + if (exceedsTolerance[0]) { + setWriteTime(n, expirationTime); + } setAccessTime(n, expirationTime); - setWriteTime(n, expirationTime); setVariableTime(n, varTime); discardRefresh(k); @@ -2552,7 +2569,7 @@ public boolean remove(Object key, Object value) { } int weightedDifference = (weight - oldWeight[0]); - if (expiresAfterWrite() || (weightedDifference != 0)) { + if (exceedsTolerance[0] || (weightedDifference != 0)) { afterWrite(new UpdateTask(node, weightedDifference)); } else { afterRead(node, now[0], /* recordHit= */ false); @@ -2573,13 +2590,15 @@ public boolean replace(K key, V oldValue, V newValue, boolean shouldDiscardRefre requireNonNull(oldValue); requireNonNull(newValue); - int weight = weigher.weigh(key, newValue); + var now = new long[1]; + var oldWeight = new int[1]; + var exceedsTolerance = new boolean[1]; @SuppressWarnings({"unchecked", "Varifier"}) @Nullable K[] nodeKey = (K[]) new Object[1]; @SuppressWarnings({"unchecked", "Varifier"}) @Nullable V[] prevValue = (V[]) new Object[1]; - int[] oldWeight = new int[1]; - long[] now = new long[1]; + + int weight = weigher.weigh(key, newValue); Node node = data.computeIfPresent(nodeFactory.newLookupKey(key), (k, n) -> { synchronized (n) { requireIsAlive(key, n); @@ -2597,8 +2616,11 @@ public boolean replace(K key, V oldValue, V newValue, boolean shouldDiscardRefre n.setWeight(weight); long expirationTime = isComputingAsync(newValue) ? (now[0] + ASYNC_EXPIRY) : now[0]; + exceedsTolerance[0] = exceedsWriteTimeTolerance(n, varTime, expirationTime); + if (exceedsTolerance[0]) { + setWriteTime(n, expirationTime); + } setAccessTime(n, expirationTime); - setWriteTime(n, expirationTime); setVariableTime(n, varTime); if (shouldDiscardRefresh) { @@ -2613,7 +2635,7 @@ public boolean replace(K key, V oldValue, V newValue, boolean shouldDiscardRefre } int weightedDifference = (weight - oldWeight[0]); - if (expiresAfterWrite() || (weightedDifference != 0)) { + if (exceedsTolerance[0] || (weightedDifference != 0)) { afterWrite(new UpdateTask(node, weightedDifference)); } else { afterRead(node, now[0], /* recordHit= */ false); @@ -2688,8 +2710,8 @@ public void replaceAll(BiFunction function) { weight[1] = weigher.weigh(key, newValue[0]); var created = nodeFactory.newNode(key, keyReferenceQueue(), newValue[0], valueReferenceQueue(), weight[1], now[0]); - setVariableTime(created, expireAfterCreate(key, newValue[0], expiry(), now[0])); long expirationTime = isComputingAsync(newValue[0]) ? (now[0] + ASYNC_EXPIRY) : now[0]; + setVariableTime(created, expireAfterCreate(key, newValue[0], expiry(), now[0])); setAccessTime(created, expirationTime); setWriteTime(created, expirationTime); return created; @@ -2724,15 +2746,11 @@ public void replaceAll(BiFunction function) { n.setValue(newValue[0], valueReferenceQueue()); n.setWeight(weight[1]); + long expirationTime = isComputingAsync(newValue[0]) ? (now[0] + ASYNC_EXPIRY) : now[0]; + setAccessTime(n, expirationTime); + setWriteTime(n, expirationTime); setVariableTime(n, varTime); - if (isComputingAsync(newValue[0])) { - long expirationTime = now[0] + ASYNC_EXPIRY; - setAccessTime(n, expirationTime); - setWriteTime(n, expirationTime); - } else { - setAccessTime(n, now[0]); - setWriteTime(n, now[0]); - } + discardRefresh(k); return n; } @@ -2853,6 +2871,7 @@ public void replaceAll(BiFunction function) { var weight = new int[2]; // old, new var cause = new RemovalCause[1]; + var exceedsTolerance = new boolean[1]; Node node = data.compute(keyRef, (kr, n) -> { if (n == null) { @@ -2925,8 +2944,11 @@ public void replaceAll(BiFunction function) { n.setWeight(weight[1]); long expirationTime = isComputingAsync(newValue[0]) ? (now[0] + ASYNC_EXPIRY) : now[0]; + exceedsTolerance[0] = exceedsWriteTimeTolerance(n, varTime, expirationTime); + if (((cause[0] != null) && cause[0].wasEvicted()) || exceedsTolerance[0]) { + setWriteTime(n, expirationTime); + } setAccessTime(n, expirationTime); - setWriteTime(n, expirationTime); setVariableTime(n, varTime); discardRefresh(kr); @@ -2954,7 +2976,7 @@ public void replaceAll(BiFunction function) { afterWrite(new AddTask(node, weight[1])); } else { int weightedDifference = weight[1] - weight[0]; - if (expiresAfterWrite() || (weightedDifference != 0)) { + if (exceedsTolerance[0] || (weightedDifference != 0)) { afterWrite(new UpdateTask(node, weightedDifference)); } else { afterRead(node, now[0], /* recordHit= */ false); diff --git a/examples/coalescing-bulkloader-reactor/gradle/libs.versions.toml b/examples/coalescing-bulkloader-reactor/gradle/libs.versions.toml index 5415fed147..299c2ec435 100644 --- a/examples/coalescing-bulkloader-reactor/gradle/libs.versions.toml +++ b/examples/coalescing-bulkloader-reactor/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] caffeine = "3.1.8" junit = "5.11.4" -reactor = "3.7.1" +reactor = "3.7.2" truth = "1.4.4" versions = "0.51.0" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 74837298ce..b32476ebd4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ auto-value = "1.11.0" awaitility = "4.2.2" bcel = "6.10.0" bnd = "7.1.0" -bouncycastle-jdk18on = "1.79" +bouncycastle-jdk18on = "1.80" cache2k = "2.6.1.Final" caffeine = "3.1.8" checkstyle = "10.21.1"