Caffeine: Size-based eviction maximumSize and removalListener didn't work

Created on 13 Mar 2019  路  3Comments  路  Source: ben-manes/caffeine

Hello @ben-manes
When I create a cache with maximumSize=1, add an removalListener. But the size-based eviction policy didn't work, the former key/value still hold, and it didn't trigger the removalListener, what I expect is that the listener should be triggered 9 times from 0 to 8.
Did I use Caffeine with wrong way? Here the code and output.

public static void main(String[] args) throws Exception {
        Cache cache = Caffeine.newBuilder()
                .maximumSize(1L)
                .removalListener((key, value, cause) -> {
                    try {
                        System.out.println(String.format("remove, key: %s value: %s cause: %s", key, value, cause));
                        TimeUnit.MINUTES.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                })
                .build();
        for (int i = 0; i < 10; i++) {
            cache.put(i + "", i);
        }
        System.out.println("cache size: " + cache.estimatedSize());
        System.out.println("key 9 value: " + cache.getIfPresent("9"));
        System.out.println("key 8 value: " + cache.getIfPresent("8"));
        System.out.println("key 7 value: " + cache.getIfPresent("7"));
        System.out.println("cache: " + cache.asMap());
    }

And the output is:

cache size: 10
key 9 value: 9
key 8 value: 8
key 7 value: 7
cache: {0=0, 1=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 9=9}
remove, key: 6 value: 6 cause: SIZE
remove, key: 7 value: 7 cause: SIZE
remove, key: 8 value: 8 cause: SIZE

But with Guava Cache, the behavior is right for me.

public static void main(String[] args) throws IOException, ExecutionException {
        Cache cache = CacheBuilder.newBuilder()
                .maximumSize(1L)
                .removalListener(n -> {
                    System.out.println(String.format("key: %s value: %s cause: %s", n.getKey(), n.getValue(), n.getCause()));
                    try {
                        TimeUnit.MILLISECONDS.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                })
                .build();

        for (int i = 0; i < 10; i++) {
            cache.put(i + "", i);
        }

        System.out.println("size: " + cache.asMap().size());
        System.out.println("key9 value: " + cache.getIfPresent("9"));
        System.out.println("key8 value: " + cache.get("8", () -> "222222222"));
        System.out.println("key7 value: " + cache.getIfPresent("7"));
        System.out.println("cache: " + cache.asMap());
    }

And the output is

remove, key: 0 value: 0 cause: SIZE
remove, key: 1 value: 1 cause: SIZE
remove, key: 2 value: 2 cause: SIZE
remove, key: 3 value: 3 cause: SIZE
remove, key: 4 value: 4 cause: SIZE
remove, key: 5 value: 5 cause: SIZE
remove, key: 6 value: 6 cause: SIZE
remove, key: 7 value: 7 cause: SIZE
remove, key: 8 value: 8 cause: SIZE
size: 1
key9 value: 9
remove, key: 9 value: 9 cause: SIZE
key8 value: loading value
key7 value: null
cache: {8=loading value}

Most helpful comment

By default we defer all work to ForkJoinPool.commonPool, whereas Guava does it on the calling thread. If you set executor(Runnable::run) then you鈥檒l see the same behavior as Guava.

All 3 comments

By default we defer all work to ForkJoinPool.commonPool, whereas Guava does it on the calling thread. If you set executor(Runnable::run) then you鈥檒l see the same behavior as Guava.

Many of the behavior differences are described in the migration guide
https://github.com/ben-manes/caffeine/wiki/Guava

Thanks, the wiki helps.

Was this page helpful?
0 / 5 - 0 ratings