cache

Cache: a Function that keeps what it answered, managed by an eviction STRATEGY

Contracts for a cache: a Cache<KEY, VALUE> is a Function from key to Optional<VALUE> with its own Capacity and EvictionStrategy, publishing CacheEvents to its OBSERVERs. This module holds the contracts, not a cache.

Rationale

Why. Code that reads, bounds or evicts from a cache should not depend on which cache it is given.

Key Concepts

Cache

Role. apply(key) answers the value when present, otherwise empty. capacity() and strategy() return the STRATEGYs that bound and evict this cache. MutableCache adds accept(key, value).

Pattern. STRATEGY

Lifecycle events

Role. Sealed CacheEvent permits Hit, Miss, Insert and Evict, so a switch over them is exhaustive. A ValueInsertion is an Insert that carries the value, and a cache may publish its own.

Pattern. OBSERVER

Capacity and eviction

Role. Each observes a cache's events. Capacity.exceeded() says whether its bound requires eviction, independently of what is chosen for removal. EvictionStrategy.evict(key) removes an entry; an absent key has no effect.

Pattern. STRATEGY, OBSERVER

How It Works

Guarantees. No public parameter takes null and no public method returns it, declared with contrax's @PreCondition(IsNotNull.class) and @PostCondition(IsNotNull.class).

Lane

Purpose. The types a cache, its capacity and its eviction are written against.

Alternative. java.util.Map when nothing is ever forgotten.

When to avoid. A value that must not be lost: a cache may forget anything it keeps.

Usage

React to a lifecycle event

import java.time.Instant;
import software.visionary.cache.events.CacheEvent;
import software.visionary.cache.events.Evict;
import software.visionary.cache.events.Hit;
import software.visionary.cache.events.Insert;
import software.visionary.cache.events.Miss;
import software.visionary.cache.events.ValueInsertion;

String described(CacheEvent<String, ?> event) {
    return switch (event) {
        case Hit<String, ?> hit -> "hit " + hit.key();
        case Miss<String, ?> miss -> "miss " + miss.key();
        case ValueInsertion<String, ?> kept -> "insert " + kept.key() + "=" + kept.value();
        case Insert<String, ?> keyOnly -> "insert " + keyOnly.key();
        case Evict<String, ?> evict -> "evict " + evict.key();
    };
}

Instant now = Instant.now();
System.out.println(described(new Miss.Occurred<>("answer", now)));
System.out.println(described(new Insert.Value<>("answer", 42, now)));
System.out.println(described(new Evict.Occurred<>("answer", now)));

Output

miss answer
insert answer=42
evict answer

A capacity of your own

final class TotalKeyLength implements Capacity<String> {
    private final int limit;
    private final Set<String> kept = new HashSet<>();

    TotalKeyLength(int limit) {
        this.limit = limit;
    }

    @Override public boolean exceeded() {
        return kept.stream().mapToInt(String::length).sum() > limit;
    }

    @Override public void update(CacheEvent<String, ?> event) {
        if (event instanceof Insert<String, ?>) {
            kept.add(event.key());
        } else if (event instanceof Evict<String, ?>) {
            kept.remove(event.key());
        }
    }
}

Instant now = Instant.now();
Capacity<String> capacity = new TotalKeyLength(9);
capacity.update(new Insert.Key<>("four", now));
capacity.update(new Insert.Key<>("five", now));
System.out.println(capacity.exceeded());
capacity.update(new Insert.Key<>("three", now));
System.out.println(capacity.exceeded());
capacity.update(new Evict.Occurred<>("four", now));
System.out.println(capacity.exceeded());

Output

false
true
false

Dependencies

  • software.visionary:events:2026.10.13 at Compile
  • software.visionary.contrax:processor:2026.10.13 at Processor

Views

Drawn from the release. A link in a view opens its source at the release tag.

architecture.svg
architecture.svg
classes.software.visionary.cache.capacity.svg
classes.software.visionary.cache.capacity.svg
classes.software.visionary.cache.events.svg
classes.software.visionary.cache.events.svg
classes.software.visionary.cache.eviction.svg
classes.software.visionary.cache.eviction.svg
classes.software.visionary.cache.svg
classes.software.visionary.cache.svg
packages.svg
packages.svg