Skip to content

No eviction with asMap.keySet.toArray #472

@zzrough

Description

@zzrough

hey,

thanks for the great library.

I'm using it for a long time, but recently I came up with something strange regarding expireAfterWrite eviction.
I don't get exactly get the semantics of the Map view (asMap), since it can or can not be up to date like the API docs tells.

But still, I've got this issue in production that puzzles me:

    public static void main(String[] args) {
        Object PRESENT = new Object();

        Cache<String,Object> cache = Caffeine.newBuilder()
                                             .expireAfterWrite(Duration.ofSeconds(3))
                                             .build();

        Consumer<Long> sleepQuietly = millis -> {
            try {
                Thread.sleep(millis);
            } catch (InterruptedException e) {
            }
        };

        new Thread(() -> {
            String name = "u1";

            for (int i = 0; i < 10; i++) {
                cache.put(name, PRESENT);
                // System.out.println(name + ": crowd = " + List.copyOf(cache.asMap().keySet()));            // ERR
                System.out.println(name + ": crowd = " + cache.asMap().keySet().stream().collect(toList())); // OK
                sleepQuietly.accept(500L);
            }
        }).start();

        new Thread(() -> {
            String name = "u2";

            sleepQuietly.accept(1000L);
            cache.put(name, PRESENT);
            // System.out.println(name + ": crowd = " + List.copyOf(cache.asMap().keySet()));            // ERR
            System.out.println(name + ": crowd = " + cache.asMap().keySet().stream().collect(toList())); // OK
        }).start();
    }

Simply put:

  • eviction is after write 3s
  • u1 thread put every 500ms
  • u2 thread put after 1s

If I use List.copyOf that relies on toArray() on the key set view, u1 never sees u2 being evicted:

u1: crowd = [u1]
u1: crowd = [u1]
u2: crowd = [u1, u2]
u1: crowd = [u1, u2]
u1: crowd = [u1, u2]
u1: crowd = [u1, u2]
u1: crowd = [u1, u2]
u1: crowd = [u1, u2]
u1: crowd = [u1, u2]
u1: crowd = [u1, u2]
u1: crowd = [u1, u2]

If the set is traversed using a stream, or the iterator in anyway, I've got the proper eviction:

u1: crowd = [u1]
u1: crowd = [u1]
u2: crowd = [u1, u2]
u1: crowd = [u1, u2]
u1: crowd = [u1, u2]
u1: crowd = [u1, u2]
u1: crowd = [u1, u2]
u1: crowd = [u1, u2]
u1: crowd = [u1, u2]
u1: crowd = [u1]
u1: crowd = [u1]

Is this normal that keySet iteration works but not the toArray method? If I use List.copyOf or new ArrayList this does not work.
I certainly was not expected this :)

thanks a lot,

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions