Caching docs: Cache is published for the pull (contract, CHANGELOG)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-25 21:39:17 +01:00
commit c8a17ccd13
2 changed files with 11 additions and 11 deletions

View file

@ -145,16 +145,16 @@ which route wrote it.
that cache's gate as its own lock, so a store's load callback into the cache
cannot take the two out of order; a cacheless store takes its own gate. The
store reads which one applies, takes it, and checks the answer again under it:
`Cache` is written only by the attach, holding both gates, once and after its
pull has succeeded (a failed attach never shows it, and nothing detaches), so
under the cache's gate `Cache` naming that cache cannot change, and under the
store's gate it cannot change at all. The pull reads the registry the attach
lent, not `Cache`; a variant it loads carries no codec, and the cache resolves
it with its own. Until `Cache` is published the store's hold is the attach's,
so a journal the pull runs cannot write the store cacheless. A call whose guess went stale (it
guessed no cache and the store then gained one) releases and retries once, so a
call makes at most two passes; a cacheless write that finds a cache is refused
in that same hold. A
`Cache` is written only by the attach, holding both gates: it publishes `Cache`
before its pull and takes it back in the same hold if the pull fails, so no
other thread ever sees the cache come and go. Under the store's gate `Cache` is
settled, and a settled cache never changes, because nothing detaches. A call
takes at most three gates: the guessed cache's, the store's, the settled
cache's. The pull reads the registry the attach lent, not `Cache`; a variant it
loads carries no codec, and the cache resolves it with its own. During an
attach the store is attached, so a journal the pull runs that calls the store
is refused as it would be afterwards; a cacheless write that finds a cache is
refused in that same hold. A
store must not call `Loaded` while holding its own lock outside the cache's
hold.
- A journal runs under the cache's gate, so a slow journal slows every writer of

View file

@ -23,7 +23,7 @@ Releases as 3.0.0 (major).
### Added
- A cacheless `WriteRecord` or `WriteDocument`, and an attach, hold the store's gate while they run, and user code they run (a document's property getters, a constructor while a copied record is read) now throws `InvalidOperationException` if it enters a cache it does not hold or a second store; it used to wait forever against an attach of the same store. A store call that queued on an attach that then failed runs under the gate that owns the store, not beside a cacheless call or a second cache's attach. An attach that fails takes its registry back, so a `PullAll` on the store afterwards is refused as unattached instead of loading records the next cache never sees. A cacheless `WriteRecord` or `WriteDocument` made while the same thread already holds that store (from a journal an attach's pull runs, or from user code another cacheless write of the store runs) throws `InvalidOperationException`; it used to write the file behind the attaching cache and take that cache's registry, so the cache could never flush or pull the store again.
- A cacheless `WriteRecord` or `WriteDocument`, and an attach, hold the store's gate while they run, and user code they run (a document's property getters, a constructor while a copied record is read) now throws `InvalidOperationException` if it enters a cache it does not hold or a second store; it used to wait forever against an attach of the same store. A store call that queued on an attach that then failed runs under the gate that owns the store, not beside a cacheless call or a second cache's attach. An attach that fails takes its registry back, so a `PullAll` on the store afterwards is refused as unattached instead of loading records the next cache never sees. A cacheless `WriteRecord` or `WriteDocument` made while the same thread already holds that store (from a journal an attach's pull runs, or from user code another cacheless write of the store runs) throws `InvalidOperationException`; it used to write the file behind the attaching cache and take that cache's registry, so the cache could never flush or pull the store again. A journal that calls its cache's store (`Push`, `Delete`, `PushAll`, `CommitBatch`, `PullAll`, `ReadStoreSnapshot`, `JudgeChecks`) while that cache's `AddBackingStore` runs the journal now throws "A journal entered the cache it journals", as it does after the attach; it used to stage or write records the cache never admitted, so the cache and the file disagreed.
- `SingleFileBackingStore.ReadStoreSnapshot()` reads a store file once, under the store's in-process gate, with the reader a cache's store opens with, and returns the durable snapshot or null when nothing is at the path. `SingleFileBackingStore.WriteRecord(record, catalogEntry, holdsIds, registry)` writes one record without a cache: under the store's lock every other record and catalog entry is copied as stored, the file is replaced atomically, and the header is decided as a flush decides it. It refuses on a store attached to a cache.
- `SingleFileBackingStore.WriteDocument<T>(key, document, storedAt, registry)` writes one typed document without a cache, as a cache write does: it plans element ids, encodes the document as `T` (the type its schema id and ids are taken from, also for a derived instance) and marks the store when it holds an id; a failed write gives the minted ids back. It refuses on a store attached to a cache. The `registry` of `WriteRecord` and `WriteDocument` serves that call only; a store keeps none.
- `CultCodec.DecodeDocument<T>(key, payload)` decodes a stored payload as a cache load does (`CultCacheMessagePack.CreateCodec(registry)` makes the codec). It reads no file and holds no state. `CultElementIds` stays internal.