Rust leaf: changelog and ownership map scope the admission refusal to ids this cache registers and state the unregistered-id consequence
All checks were successful
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Successful in 19m30s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Libby 2026-11-11 23:46:17 +00:00
commit 293ce32a6b
2 changed files with 5 additions and 2 deletions

View file

@ -66,7 +66,10 @@ What this establishes:
hold (a fresh key, or one another writer filled after this cache's pull) the single-file store's commit guard judges by
the stored and claimed schema ids, so an envelope claiming another registered type's id is refused earlier, at every
envelope admission (`refuse_claimed_id`, the pull's own `resolve_registered_type` lookup); an id naming no registered type
passes. Store-level writers called directly (`push`, `insert_entry_if_absent`, compare-and-swap) trust the claimed id.
passes, trusted as claimed: an envelope under such an id can replace another writer's record stored under it at a key
this cache does not hold, or be served as another type by a reader that registers the type the id names (Soul g8a, g8c,
open: which ids a Rust type owns is not declared to CultCache). Store-level writers called directly (`push`,
`insert_entry_if_absent`, compare-and-swap) trust the claimed id.
The cache carries an unregistered type's records as foreign and walks the vector's 12 rows.
- Interop (unproven): a Python store holding A and B at k fails C# decode first on payload encoding (Python's
lambda-defined document wrote JSON payloads), so what C# does with two records at one key was not reached.

View file

@ -17,7 +17,7 @@ only in git history.
- A single-file store holds one record per key. The v1 and legacy readers refuse a store with two records under one key (`CultCacheStoreUnreadable`, the record `Undecodable`): the store does not open, and a write over it refuses and leaves the file. A consumer that holds such a file must repair it with the runtime that wrote it before opening it here.
- Every single-file writer (`push`, `push_all`, `insert_entry_if_absent`, the compare-exchange and snapshot-append family, and the cache's puts and batches) refuses to put a second type under a key the store holds, with `SchemaConflictError` naming the key and both types, writing nothing. Retyping a key is a delete, then an add (one batch may hold both). A write owns the held record when its schema id equals the id the held record is stored under, as C# `WrittenOwns` asks; a type renamed under a stable id therefore replaces its own record, and a write of another type name under another id is refused (`push` of the same type name under another id still replaces the record, as before; `compare_and_swap_entry`, and a `push_all` batch holding one type under two ids, refuse it). Registered types here declare no compatible ids, so equality is the whole test. Writers used to leave two records under the key.
- `compare_and_swap_entry` over a single file refuses an in-place retype (expected `A/k`, replacement `B/k` under another schema id) with the same `SchemaConflictError`, as `compare_exchange` does, where it used to retype the record and return `true`. A lost race still returns `false` first.
- A cache holds one record per key. `put`, `put_envelope`, `put_raw_envelope` and a batch of a second type under a key the cache holds, or a key a store carries foreign, are refused with `SchemaConflictError` even with no store (a memory-only cache used to hold both), whatever schema id the envelope claims: another registered type's record is never owned. `put_envelope`, `put_raw_envelope`, `put_prepared_batch` and `load_soa` also refuse, at any key, an envelope whose schema id names another registered type (the id a pull would resolve the record by), so a stale cache or a fresh key cannot store one type under another's id; an id naming no registered type still passes. Store-level writers called directly (`push`, `insert_entry_if_absent`, compare-and-swap) still trust the claimed id. A refused write or pull changes nothing. Two stores holding one key refuse the pull, naming the key and both types, and so does one store holding two records at a key that resolve to one type. A store holding a record no registered type claims now loads (it used to refuse with "No schema is registered for persisted entry type") and the record is carried, through `put_prepared_batch` too, which lays foreign records back byte for byte; `delete` at a foreign key is refused. `load_soa` refuses a second type at a key or a record at a foreign key, and still refuses an unregistered type. The redb stores still key by `(type, key)`, and their own writers (`push`, `insert_entry_if_absent`, compare-and-swap) still put a second type at a held key: a cache now refuses to pull such a redb store with `SchemaConflictError`, where it used to load both records.
- A cache holds one record per key. `put`, `put_envelope`, `put_raw_envelope` and a batch of a second type under a key the cache holds, or a key a store carries foreign, are refused with `SchemaConflictError` even with no store (a memory-only cache used to hold both), whatever schema id the envelope claims: another registered type's record is never owned. `put_envelope`, `put_raw_envelope`, `put_prepared_batch` and `load_soa` also refuse, at any key, an envelope whose schema id names another type registered in this cache (the id a pull would resolve the record by). An id this cache does not register is trusted as the envelope claims it: at a key this cache does not hold, the single-file store decides ownership by that id, so such an envelope can replace another writer's record stored under it, and a reader registering the type the id names serves the record as that type. Store-level writers called directly (`push`, `insert_entry_if_absent`, compare-and-swap) still trust the claimed id. A refused write or pull changes nothing. Two stores holding one key refuse the pull, naming the key and both types, and so does one store holding two records at a key that resolve to one type. A store holding a record no registered type claims now loads (it used to refuse with "No schema is registered for persisted entry type") and the record is carried, through `put_prepared_batch` too, which lays foreign records back byte for byte; `delete` at a foreign key is refused. `load_soa` refuses a second type at a key or a record at a foreign key, and still refuses an unregistered type. The redb stores still key by `(type, key)`, and their own writers (`push`, `insert_entry_if_absent`, compare-and-swap) still put a second type at a held key: a cache now refuses to pull such a redb store with `SchemaConflictError`, where it used to load both records.
- A write of the same type under a held key replaces the record by key, and in one `push_all` the last given of several entries under a key and a schema id is the record, whatever their type names. The redb stores are unchanged and still key by type and key.
### Added