fix: validate ttl_nanos when reusing a shared region across processes (#42)#80
Merged
Conversation
…#42) create_or_open's reuse check compared version, capacity, max_key_size, and max_value_size but not ttl_nanos. A process opening an existing shared cache with a different TTL (e.g. ttl=None when the creator used ttl=10) silently reused the creator's region and inherited its TTL from the shared header — expiry is read from h.ttl_nanos at lookup time, so the second process's requested TTL was ignored. Same decorator params produced different effective behavior depending on which process won the create race, and it was inconsistent with the other config params (which force a recreate on mismatch). Add `header.ttl_nanos == ttl_nanos` to the reuse condition so a TTL mismatch recreates the region, exactly like a capacity/key/value-size mismatch (last-writer-wins). Add a regression test: a region created with ttl=0.1 then opened with ttl=None must honor ttl=None (entry survives past 0.1s) — before the fix it expired per the creator's TTL. The bug is in the reuse decision, exercised on the second construction, so a single-process test covers it deterministically. Also repair a merge artifact in ARCHITECTURE.md: the issue #37 invariant text had been stranded as a duplicated fragment after the #40 bullet (from #76 and #77 landing together); fold it back into the #[repr(C)] bullet. Document the config-gates-reuse invariant. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #42
What & why
region.rs::create_or_opendecides whether to reuse an existing shm region by comparingversion,capacity,max_key_size, andmax_value_size— but notttl_nanos. TTL lives in the shared header and is read at lookup time (shm/mod.rs:h.ttl_nanos), governing expiry for every process. So when process B opens a cache that process A created with a different TTL:cache(backend="shared", ttl=10)(f)shm_name):cache(backend="shared", ttl=None)(f)→ silently reuses A's region; B's entries expire per ttl=10, not None.Same decorator params → different effective behavior depending on who won the create race, and inconsistent with the other config params (which already force a recreate on mismatch).
The fix
Add
header.ttl_nanos == ttl_nanosto the reuse condition, so a TTL mismatch recreates the region — same last-writer-wins behavior as a capacity/key/value-size mismatch.Test
TestSharedTTLConfigMismatch::test_ttl_mismatch_recreates_region: construct a region withttl=0.1, then construct again on the sameshm_namewithttl=None; an entry stored by the second must survive past 0.1s. The bug lives in the reuse decision (hit on the second construction, same code path in-process or cross-process), so a single-process test covers it deterministically.Verified fail-before → pass-after by stashing just the fix: buggy build gave
hits=0, misses=2(entry expired per the creator'sttl=0.1); with the fix,hits=1.Drive-by doc fix (disclosed)
While adding the #42 invariant I found a merge artifact in
docs/ARCHITECTURE.md: the issue #37 invariant text had been stranded as a duplicatedalignment padding; …fragment after the #40 bullet (a result of #76 and #77 landing together). I folded it back into the#[repr(C)]bullet where it belongs. Pure docs, no behavior — happy to split into its own PR if you'd prefer.Gates run (risky change —
src/shm/region.rs)make fmt/make lint(ruff, ty, clippy-D warnings) ✓make test—cargo test(11) + pytest (102, +1 new) ✓make test-matrix— Python 3.10–3.13 ✓, new test passes on each (3.14 skipped locally via the documenteduv-resolves-stale-alpha guard; CI covers 3.14 final)make bench— no regression (construction-time-only change): shared backend ~9.4M ops/s, hit-rate 72.9%🤖 Generated with Claude Code