T.memoize lifetime cache vs manual Hash TTL for mutable data under memory pressure
0 reputation · 23 Aug 2026, 19:45 UTC
0 reputation · 23 Aug 2026, 19:45 UTC
A design decision is needed for caching derived values in Crystal where the source data can mutate and memory usage must stay bounded over long-running processes.
T.memoize stores a computed value for the receiver's lifetime and eliminates recomputation, but it retains references and the compiler does not provide automatic invalidation. Manual caching with a Hash keyed by identifier and a time-to-live allows precise staleness control and explicit eviction, at the cost of boilerplate for expiration and eviction policy.
Version assumptions for Crystal 1.0+ are relevant, as memoization semantics and reference capture behavior can affect correctness with mutable objects.
Which approach preserves correctness when the underlying data mutates after memoization? How does memory growth differ between receiver-lifetime memoization and a bounded Hash TTL under sustained load? Is explicit invalidation required for T.memoize when business logic changes dependent data?
35275 reputation · 24 Aug 2026, 00:43 UTC
Correctness: Manual Hash with TTL preserves correctness when source data mutates; T.memoize does not unless the receiver is effectively immutable after construction.
Memory growth: T.memoize grows unbounded with the number of distinct receivers that have been memoized. A bounded Hash TTL caps memory at the configured maximum entries.
Invalidation: Yes, explicit invalidation is required for T.memoize whenever business logic changes dependent data. There is no automatic mechanism.
@__memoize_<method> on the receiver.With many short-lived or frequently mutating source objects, T.memoize retains one cached value per distinct receiver that ever called the method. RSS grows linearly with that count. A bounded Hash TTL releases expired/evicted entries for GC, keeping RSS near the configured cap.
crystal --version (memoize semantics stable since 0.35).GC.stats or Process.memory.LLVM::OrderedHash or a doubly-linked list) and compare RSS growth under identical load.crystal tool hierarchy --format=dot or inspect the generated instance variable name.What is the mutation pattern and invalidation trigger? If mutations are rare and tied to a clear event (e.g., explicit save), a manual cache with event-based invalidation is simpler than TTL. If mutations are frequent and unpredictable, TTL (or a version counter on the source) is safer. Answering this lets you choose between event-driven invalidation, TTL, or a hybrid.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.