Contract and CHANGELOG: a one-batch retype is an eviction then an add, a rebase follows the plain-write rule, the protected per-key judge
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
1ff5ae79dc
commit
2239fb9b5f
2 changed files with 2 additions and 1 deletions
|
|
@ -213,7 +213,7 @@ compatible), or when it is the type that reads the stored record's published ent
|
|||
id of its own schema name that it reads whole is its own and is restamped. Anything else is refused, another registered type's record
|
||||
included, and so is a record the written type would read only by dropping persisted slots. No registry enters the decision, `Shared` included:
|
||||
the answer for a given written entry, stored id and catalog is the same lent a registry or not, and a cache's registered types do not make
|
||||
each other's records writable. Retyping a key is a removal and an add: in one batch (the removal called before the add) or staged apart, with or without a backing store. The removal half is judged as a removal, so the key is retyped only when the removal rule allows it. A removal or check of a key goes to the
|
||||
each other's records writable. Retyping a key is a removal and an add: in one batch (the removal called before the add) or staged apart, with or without a backing store. The removal half is judged as a removal, so the key is retyped only when the removal rule allows it. A one-batch retype is staged as the held record's eviction and then the add: a watcher of the old type sees Removed and a watcher of the new type sees Added, a global's slot is freed, and every check, validator and store judge sees what it sees when the two are staged apart; this holds for plain records, variants and flattens alike (a Flatten after a Remove in the same batch flattens nothing and is refused). A variant rebased onto a base of another type follows the plain-write rule: where a plain write of the new type would own the variant's record (the new type declares the old id) the rebase commits and the variant becomes that type, and where a plain write would be refused the rebase is refused. A removal or check of a key goes to the
|
||||
registry's removal rule (a record a registered type declares, or reads whole, may be removed), and a cacheless call, which has no registry,
|
||||
removes a record only when an entry it writes owns it; a call that writes no entry removes nothing.
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ Releases as 3.0.0 (major).
|
|||
|
||||
### Added
|
||||
|
||||
- `CacheBackingStore.RefuseUndeclaredOnDisk(written, catalog, heldAt, writtenAt, removed, durableAt, foreignAt, unstage, cacheless)` is the one protected per-key ownership judge for a store kind outside the library: it applies the whole rule under the store's lock (the written type owns a record by its own id, an id it lists or an older id of its own name it reads whole; a key the write removes or checks, and a key the cache removed since the last flush, is owned by any registered type that declares or reads the record whole) and refuses through `Refuse`. Both shipped stores call it. Additive: the earlier overload taking a store-built `undeclaredAt` judge is unchanged. A batch that removes a key and adds a record there is staged as the held record's eviction and then the add, so a watcher of the old type sees `Removed`, a watcher of the new type sees `Added`, and a variant rebase onto a base of another type changes type exactly where a plain write may.
|
||||
- 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 user code another cacheless write of the store runs, or from a journal an attach's pull runs (then "The store is attached to a cache, write through the cache.", as after the attach) 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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue