functools.lru_cache with cache_clear() or functools.cache: bounded memory versus simpler caching for mutable external state
0 reputation · 16 Apr 2023, 21:07 UTC
0 reputation · 16 Apr 2023, 21:07 UTC
In a long-running Python process that repeatedly calls a function with identical arguments, the choice between functools.lru_cache(maxsize=128) paired with explicit cache_clear() and functools.cache hinges on two competing constraints: keeping resident memory bounded while ensuring cached results do not outlive changes to external mutable state such as files, database rows, or global variables.
lru_cache with a finite maxsize evicts least-recently-used entries, but it keys only on the arguments passed to the wrapped call and does not detect when those external dependencies change; stale values persist until cache_clear() is invoked, which invalidates every entry and sacrifices the hit rate for unrelated argument combinations that remain valid. functools.cache, available from Python 3.9, behaves like lru_cache(maxsize=None): it offers no maxsize parameter, never evicts entries, and can grow without bound under the same external-change conditions. Neither decorator expires entries by time or invalidates automatically when dependencies change.
Given that cache_clear() is an all-or-nothing operation whose effect on concurrent readers is not fully specified at the application level, and that functools.cache is unavailable before Python 3.9, which documented approach better serves a process where memory must stay bounded but external state changes unpredictably? Should the decision change when the number of distinct argument tuples is provably small, or when invalidation can be coordinated across threads?
35775 reputation · 16 Apr 2023, 21:37 UTC
When memory must stay bounded and the wrapped function’s arguments may become stale because of external mutable data, lru_cache(maxsize=N) is the documented option that can satisfy both constraints, whereas functools.cache offers no size limit and can grow without bound.
If you can reliably coordinate invalidation across threads or have a deterministic way to detect external changes, the need for frequent cache_clear() calls diminishes and the bounded‑memory benefit of lru_cache remains sufficient. Conversely, if the number of distinct argument combinations is tiny, setting maxsize to that count eliminates the need for manual clearing altogether.
Please confirm whether you have a dependable mechanism for detecting external state changes (e.g., file timestamps, DB change events) or if you can coordinate cache clearing across threads, as this detail will affect the optimal caching strategy.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.