mirror of
https://github.com/element-hq/synapse.git
synced 2026-11-12 01:17:40 +00:00
Invalidation stays in Python. Synapse invalidates caches by *name*: it resolves `getattr(store, cache_name)` and calls `.invalidate(key)` on whatever it finds. So `RustCache`, a `#[pyclass]` exposing that protocol, bound onto a datastore under the right attribute name, inherits the whole existing path (the replication stream, `txn.call_after`, `invalidate_all`) with no changes to the machinery. Keys arrive as strings from other workers, because the stream's column is `keys text[]`, and each key type decodes them back via `PyCacheKey`. A key it cannot decode matches nothing, as it would for a Python cache, rather than failing the batch of invalidations it arrived in. Every cache belongs to one homeserver's `RustRuntime`, whose `CacheRegistry` builds it and is what expires it, so nothing here is process-wide. Tests run several homeservers in one process, and one homeserver's expiry tick must not reach another's caches. Entries expire by age like Python's, off the same 30 second looping call and the same `expire_caches` / `cache_entry_ttl` options, but per cache rather than via a global linked list, since a cache's LRU order is already its age order. Memory-pressure eviction is not implemented. That one does need a global coldest-first order, which a k-way merge over the per-cache LRU tails can give without a global list. Rust keeps the hit, miss and eviction counters. `register_rust_cache` registers a cache with the usual `CacheMetric` and copies them onto it at each scrape, and wires `set_cache_factor` up so that `SYNAPSE_CACHE_FACTOR_*` and `caches.per_cache_factors` reach a Rust cache the same way. There are still no caches. The next commit adds the first. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ULdieJyQoHNzeHq42H2M1p