diff --git a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/LocalLoadingCache.java b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/LocalLoadingCache.java index aa8f33fb2c..c0f7e1f92b 100644 --- a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/LocalLoadingCache.java +++ b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/LocalLoadingCache.java @@ -98,18 +98,20 @@ default Map loadSequentially(Iterable keys) { /** Batch loads the missing entries. */ default Map loadInBulk(Iterable keys) { - Map found = cache().getAllPresent(keys); Set keysToLoad = new LinkedHashSet<>(); + Map found = cache().getAllPresent(keys); + Map result = new LinkedHashMap<>(found.size()); for (K key : keys) { - if (!found.containsKey(key)) { + V value = found.get(key); + if (value == null) { keysToLoad.add(key); } + result.put(key, value); } if (keysToLoad.isEmpty()) { return found; } - Map result = new LinkedHashMap<>(found); bulkLoad(keysToLoad, result); return Collections.unmodifiableMap(result); } @@ -129,7 +131,9 @@ default void bulkLoad(Set keysToLoad, Map result) { }); for (K key : keysToLoad) { V value = loaded.get(key); - if (value != null) { + if (value == null) { + result.remove(key); + } else { result.put(key, value); } } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncLoadingCacheTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncLoadingCacheTest.java index bbaf39c95e..1caa84199d 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncLoadingCacheTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/AsyncLoadingCacheTest.java @@ -285,6 +285,21 @@ public void getAllPresent_ordered_absent( assertThat(result, is(equalTo(keys))); } + @CheckNoWriter + @Test(dataProvider = "caches") + @CacheSpec(loader = { Loader.NEGATIVE, Loader.BULK_NEGATIVE }, + population = { Population.SINGLETON, Population.PARTIAL }, + removalListener = { Listener.DEFAULT, Listener.REJECTING }) + public void getAllPresent_ordered_partial( + AsyncLoadingCache cache, CacheContext context) { + List keys = new ArrayList<>(context.original().keySet()); + keys.addAll(context.absentKeys()); + Collections.shuffle(keys); + + List result = new ArrayList<>(cache.getAll(keys).join().keySet()); + assertThat(result, is(equalTo(keys))); + } + @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(loader = { Loader.NEGATIVE, Loader.BULK_NEGATIVE }, diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedLocalCacheTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedLocalCacheTest.java index a31f380bf3..33c1d68065 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedLocalCacheTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/BoundedLocalCacheTest.java @@ -620,13 +620,7 @@ public void adapt_increaseWindow(Cache cache, CacheContext con long windowSize = localCache.windowWeightedSize(); long windowMaximum = localCache.windowMaximum(); - localCache.setPreviousSampleHitRate(0.80); - localCache.setMissesInSample(sampleSize / 2); - localCache.setHitsInSample(sampleSize - localCache.missesInSample()); - localCache.climb(); - - // Fill main protected space - cache.asMap().keySet().stream().forEach(cache::getIfPresent); + adapt(cache, localCache, sampleSize); assertThat(localCache.mainProtectedMaximum(), is(either(lessThan(protectedMaximum)).or(is(0L)))); @@ -650,13 +644,7 @@ public void adapt_decreaseWindow(Cache cache, CacheContext con long windowSize = localCache.windowWeightedSize(); long windowMaximum = localCache.windowMaximum(); - localCache.setPreviousSampleHitRate(0.80); - localCache.setMissesInSample(sampleSize / 2); - localCache.setHitsInSample(sampleSize - localCache.missesInSample()); - localCache.climb(); - - // Fill main protected space - cache.asMap().keySet().stream().forEach(cache::getIfPresent); + adapt(cache, localCache, sampleSize); assertThat(localCache.mainProtectedMaximum(), is(greaterThan(protectedMaximum))); assertThat(localCache.mainProtectedWeightedSize(), is(greaterThan(protectedSize))); @@ -680,4 +668,15 @@ private BoundedLocalCache prepareForAdaption( cache.asMap().keySet().stream().forEach(cache::getIfPresent); return localCache; } + + private void adapt(Cache cache, + BoundedLocalCache localCache, int sampleSize) { + localCache.setPreviousSampleHitRate(0.80); + localCache.setMissesInSample(sampleSize / 2); + localCache.setHitsInSample(sampleSize - localCache.missesInSample()); + localCache.climb(); + + // Fill main protected space + cache.asMap().keySet().stream().forEach(cache::getIfPresent); + } } diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LoadingCacheTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LoadingCacheTest.java index 1a7604f10e..172d73945e 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LoadingCacheTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/LoadingCacheTest.java @@ -288,6 +288,21 @@ public void getAllPresent_ordered_absent( assertThat(result, is(equalTo(keys))); } + @CheckNoWriter + @Test(dataProvider = "caches") + @CacheSpec(loader = { Loader.NEGATIVE, Loader.BULK_NEGATIVE }, + population = { Population.SINGLETON, Population.PARTIAL }, + removalListener = { Listener.DEFAULT, Listener.REJECTING }) + public void getAllPresent_ordered_partial( + LoadingCache cache, CacheContext context) { + List keys = new ArrayList<>(context.original().keySet()); + keys.addAll(context.absentKeys()); + Collections.shuffle(keys); + + List result = new ArrayList<>(cache.getAll(keys).keySet()); + assertThat(result, is(equalTo(keys))); + } + @CheckNoWriter @Test(dataProvider = "caches") @CacheSpec(loader = { Loader.NEGATIVE, Loader.BULK_NEGATIVE },