Skip to content

Commit 754b19d

Browse files
committed
Add clarifying JavaDoc (see #537)
In #537, a deadlock occurs because the reload operation blocks waiting for other reloads to complete. This is to batch the individual reloads through a blocking queue, which drains tasks as work is performed. Due to in-flight reloads being tracked by a secondary map, the triggering of a reload is performed with a map computation. This ensures that a stale reload does not insert outdated data by the entry being removed when the reload completes or the original entry was removed / modified. Due to a reload blocking on a map lock, when the batch completes the future's callback to remove the entry collides on the same map lock due to hashing. This causes the task queue to never be reduced to allow more work, so the reload holding the lock is stuck forever. Due to hash collisions, other usages of the map (like eviction) may collide and fully disrupt the cache from performing any more writes.
1 parent 7b28541 commit 754b19d

File tree

3 files changed

+18
-0
lines changed

3 files changed

+18
-0
lines changed

caffeine/src/main/java/com/github/benmanes/caffeine/cache/AsyncCacheLoader.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ public interface AsyncCacheLoader<K extends Object, V extends Object> {
4848

4949
/**
5050
* Asynchronously computes or retrieves the value corresponding to {@code key}.
51+
* <p>
52+
* <b>Warning:</b> loading <b>must not</b> attempt to update any mappings of this cache directly.
5153
*
5254
* @param key the non-null key whose value should be loaded
5355
* @param executor the executor with which the entry is asynchronously loaded
@@ -71,6 +73,8 @@ public interface AsyncCacheLoader<K extends Object, V extends Object> {
7173
* This method should be overridden when bulk retrieval is significantly more efficient than many
7274
* individual lookups. Note that {@link AsyncLoadingCache#getAll} will defer to individual calls
7375
* to {@link AsyncLoadingCache#get} if this method is not overridden.
76+
* <p>
77+
* <b>Warning:</b> loading <b>must not</b> attempt to update any mappings of this cache directly.
7478
*
7579
* @param keys the unique, non-null keys whose values should be loaded
7680
* @param executor the executor with which the entries are asynchronously loaded
@@ -92,6 +96,9 @@ public interface AsyncCacheLoader<K extends Object, V extends Object> {
9296
* {@code null} is computed. This method is called when an existing cache entry is refreshed by
9397
* {@link Caffeine#refreshAfterWrite}, or through a call to {@link LoadingCache#refresh}.
9498
* <p>
99+
* <b>Warning:</b> loading <b>must not</b> attempt to update any mappings of this cache directly
100+
* or block waiting for other cache operations to complete.
101+
* <p>
95102
* <b>Note:</b> <i>all exceptions thrown by this method will be logged and then swallowed</i>.
96103
*
97104
* @param key the non-null key whose value should be loaded

caffeine/src/main/java/com/github/benmanes/caffeine/cache/Cache.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ Map<K, V> getAll(Iterable<? extends K> keys,
223223
* Returns access to inspect and perform low-level operations on this cache based on its runtime
224224
* characteristics. These operations are optional and dependent on how the cache was constructed
225225
* and what abilities the implementation exposes.
226+
* <p>
227+
* <b>Warning:</b> policy operations <b>must not</b> be performed from within an atomic scope of
228+
* another cache operation.
226229
*
227230
* @return access to inspect and perform advanced operations based on the cache's characteristics
228231
*/

caffeine/src/main/java/com/github/benmanes/caffeine/cache/CacheLoader.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ default CompletableFuture<? extends V> asyncLoad(K key, Executor executor) {
123123
* This method should be overridden when bulk retrieval is significantly more efficient than many
124124
* individual lookups. Note that {@link AsyncLoadingCache#getAll} will defer to individual calls
125125
* to {@link AsyncLoadingCache#get} if this method is not overridden.
126+
* <p>
127+
* <b>Warning:</b> loading <b>must not</b> attempt to update any mappings of this cache directly.
126128
*
127129
* @param keys the unique, non-null keys whose values should be loaded
128130
* @param executor the executor that with asynchronously loads the entries
@@ -151,6 +153,9 @@ default CompletableFuture<? extends V> asyncLoad(K key, Executor executor) {
151153
* returned. This method is called when an existing cache entry is refreshed by
152154
* {@link Caffeine#refreshAfterWrite}, or through a call to {@link LoadingCache#refresh}.
153155
* <p>
156+
* <b>Warning:</b> loading <b>must not</b> attempt to update any mappings of this cache directly
157+
* or block waiting for other cache operations to complete.
158+
* <p>
154159
* <b>Note:</b> <i>all exceptions thrown by this method will be logged and then swallowed</i>.
155160
*
156161
* @param key the non-null key whose value should be loaded
@@ -172,6 +177,9 @@ default CompletableFuture<? extends V> asyncLoad(K key, Executor executor) {
172177
* {@code null} is computed. This method is called when an existing cache entry is refreshed by
173178
* {@link Caffeine#refreshAfterWrite}, or through a call to {@link LoadingCache#refresh}.
174179
* <p>
180+
* <b>Warning:</b> loading <b>must not</b> attempt to update any mappings of this cache directly
181+
* or block waiting for other cache operations to complete.
182+
* <p>
175183
* <b>Note:</b> <i>all exceptions thrown by this method will be logged and then swallowed</i>.
176184
*
177185
* @param key the non-null key whose value should be loaded

0 commit comments

Comments
 (0)