Skip to content

Cache.keys() exposes a live, mutable view of DefaultCache's internal map #135

Description

@dmccoystephenson

DefaultCache.keys() returns records.keySet() directly:

@Override
public Set<K> keys() {
    return records.keySet();
}

records is a ConcurrentHashMap, so the returned value is a KeySetView backed by that map rather than a snapshot. Two consequences follow for any caller:

  1. The set is live. A set obtained before a set or remove call reflects the change afterwards. A caller that holds the result and iterates it later does not see the cache as it stood when keys() was called.
  2. The set is mutable, and mutating it mutates the cache. cache.keys().remove(someKey) and cache.keys().clear() evict entries from the cache itself, bypassing the Cache interface entirely. Nothing in the interface signals that keys() hands out write access to the cache's contents.

COMMANDS.md documents the method as "Returns the set of all keys currently in the cache", which reads as a snapshot and does not describe either behaviour.

Suggested resolution

Returning Set.copyOf(records.keySet()) or Collections.unmodifiableSet(records.keySet()) would close the write path. The former also makes the result a snapshot; the latter keeps it live but read-only. Either is a behaviour change for any downstream consumer relying on the current semantics, so it is worth deciding deliberately — ponder-cache is published to repo.dansplugins.com and consumed by other projects.

Origin

This was surfaced while characterization tests were being written for ponder-cache in #134. Under that cycle's test-expansion rules production code is not changed and current behaviour is not cemented by assertion, so the leak is neither fixed nor asserted there; it is filed here instead.

This issue body was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).


drafted by Claude on behalf of Daniel Stephenson

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions