CHANGELOGs list the measured public surface against 2.0.1 (apicompat strict): members on their owning types, Id members in Mesh, Networking and Geometry, Rust SchemaConflictError, cultnet-rs register
The Unity list no longer calls MarkFlushSucceeded removed and places HeaderFor and the CacheBackingStore members where apicompat finds them. cultnet-rs gains a CHANGELOG. The store-composition contract moves DecodeDocument to the codec and states that a call's registry serves that call only. Docs only. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d1dfd8f027
commit
a056918be6
5 changed files with 39 additions and 12 deletions
|
|
@ -11,6 +11,10 @@ only in git history.
|
|||
- A single-file store written as `cultcache.store.v3` is read and written: a record that holds an element id (an owned list element with an `[CultElementId]` member, carried through every write) marks the store v3, and a v3 store is carried through a rewrite with its ids. A runtime from before this change refuses a v3 store by name.
|
||||
- A write over a file the reader refuses (a zero-byte file, a truncated store, a stamp this runtime does not read) refuses with `CultCacheStoreUnreadable` and leaves the file as it is. A whole-store write used to replace such a file. This reverses 0.3.1, where `push_all` replaced a file with no readable stamp (healing it); the stamp rule now has one owner, the reader every write asks under the lock, and it accepts v1 and v3 (0.3.1 refused v3).
|
||||
|
||||
### Added
|
||||
|
||||
- `SchemaConflictError`, the typed refusal of a write the catalog cannot describe: records of different types under one schema id, or an arrived schema and a registered one that share an id and disagree on the schema name. A whole-store write (`push_all`) refuses with it, names the schema id, the names involved and a record key, writes nothing and leaves the file as it is. A caller tells it apart with `error.downcast_ref::<SchemaConflictError>()`.
|
||||
|
||||
## [0.3.1]
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ All notable changes to `@gamecult/cultcache-ts` are recorded here, following
|
|||
- A single-file store written as `cultcache.store.v3` is read and written: a record that holds an element id (an owned list element with an `[CultElementId]` member, carried through every write) marks the store v3, and a v3 store is carried through a rewrite with its ids. A runtime from before this change refuses a v3 store by name.
|
||||
- A single-file store refuses a zero-byte file as `StoreUnreadableError`; only a path with nothing at it is an empty store. A header this runtime does not read is refused, and the refusal echoes the header only as `cultcache.store.v<digits>`.
|
||||
- A write over a file the reader refuses (`StoreUnreadableError`, from `push`, `pushAll` and `delete`) refuses and leaves the file as it is. A flush used to replace such a file. A write set cannot merge with a disk it cannot read, so the file is repaired or removed by its owner before the store writes again.
|
||||
- This reverses the 0.14.2 `pushAll` fix in two ways. A full `pushAll` over a file with no readable stamp no longer replaces it (healing it): the reader refuses it, and the file is left as it is. And a `cultcache.store.v3` stamp is read, not refused. The stamp rule has one owner, `refuseUnreadFormat`, asked only by the one reader that every write consults under the lock.
|
||||
- This reverses the 0.14.2 `pushAll` fix in two ways. A full `pushAll` over a file with no readable stamp no longer replaces it (healing it): the reader refuses it, and the file is left as it is. And a `cultcache.store.v3` stamp is read, not refused. The stamp rule has one owner, `refuseUnreadFormat`, asked only by the one reader that every write consults under the lock. The reader judges the stamp from the header before it decodes the body, so a newer store is refused by name whether or not its body is whole.
|
||||
- A dangling symbolic link at the store path is an I/O error with code `ENOENT`, not an empty store.
|
||||
|
||||
### Added
|
||||
|
|
|
|||
12
packages/cultnet-rs/CHANGELOG.md
Normal file
12
packages/cultnet-rs/CHANGELOG.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to `cultnet-rs` are recorded here, following
|
||||
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Earlier releases are recorded
|
||||
only in git history.
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Breaking
|
||||
|
||||
- `CultNetDocumentRegistry::register` returns `Result<&mut Self>` where it returned `&mut Self`. A binding that claims a document type or a schema id another binding already holds is refused with `cultcache_rs::SchemaConflictError` (naming the new binding's schema id and both document types) and nothing is bound; binding a type to the schema id it already holds still replaces that binding. Callers chain `register(...)?`.
|
||||
- `CultMeshCdnChunkRef` gains a public `id` field, the decimal text of `offset` (the reference's element id, slot 4). A manifest written before ids reads `""`; encoding writes the slot.
|
||||
|
|
@ -351,17 +351,22 @@ no serialization; a CultMath encoding, if added, uses this shape.
|
|||
|
||||
A single-file store file can be read and written without a cache, through public members of `SingleFileBackingStore` (for
|
||||
example `SingleFileMessagePackBackingStore`): `ReadStoreSnapshot()`, `WriteRecord(record, catalogEntry, holdsIds, registry)`,
|
||||
`WriteDocument<T>(key, document, storedAt, registry)` and `DecodeDocument<T>(key, payload, registry)`.
|
||||
and `WriteDocument<T>(key, document, storedAt, registry)`. Decoding a stored payload needs no file, so it is on the codec, not the store:
|
||||
`CultCodec.DecodeDocument<T>(key, payload)` (`CultCacheMessagePack.CreateCodec(registry)` makes the codec).
|
||||
|
||||
- `ReadStoreSnapshot()` reads the file once, under the store's gate, and returns the durable snapshot (records, schema catalog,
|
||||
format stamp), or null when nothing is at the path. It throws `CultStoreUnreadableException` for a file the reader refuses.
|
||||
- `WriteRecord` writes one record under the store's lock: the record replaces the one at its key, every other record and catalog
|
||||
entry is copied forward as stored, and the file is replaced atomically. It takes a payload the caller already encoded, so a
|
||||
payload it cannot read leaves the header as the file has it (`holdsIds` null).
|
||||
- `WriteDocument<T>` writes one typed document as a cache write does: it plans element ids, encodes the document with the
|
||||
registry, and stores it through `WriteRecord`. A failed write gives the minted ids back.
|
||||
- `DecodeDocument<T>` decodes a payload read from the store as a cache load does: it mints the ids a load mints and refuses what
|
||||
a cache load refuses.
|
||||
- `WriteDocument<T>` writes one typed document as a cache write does: it plans element ids, encodes the document as `T` (the type
|
||||
its schema id and ids are taken from; a derived instance is stored as `T`) with the registry, and stores it as `WriteRecord`
|
||||
does. A failed write gives the minted ids back.
|
||||
- `CultCodec.DecodeDocument<T>` decodes a payload read from the store as a cache load does: it mints the ids a load mints and
|
||||
refuses what a cache load refuses. It reads no file and holds no state.
|
||||
|
||||
The `registry` argument of `WriteRecord` and `WriteDocument` serves that one call: the store attaches it under its gate for the call
|
||||
and keeps none, so the same call decides the same header on a fresh instance and on one that has served other calls.
|
||||
|
||||
They share everything with a cache's store and nothing is re-implemented beside it: one reader (the stamp is judged before the
|
||||
body, and a file the reader refuses is refused by a read and by a write alike), the store's lock, the atomic replace, and the
|
||||
|
|
@ -370,7 +375,7 @@ keeps whatever the file already says). Ownership by declaration binds a cacheles
|
|||
the written catalog entry does not own or list as compatible is refused with `CultSchemaConflictException`, and the file is left as
|
||||
it was. Element-id planning (`CultElementIds`) is internal to the library: a caller never plans or marks ids, the store does. A store
|
||||
attached to a cache refuses `WriteRecord` and `WriteDocument` (write through the cache, whose staged writes land through the
|
||||
same private write) and `DecodeDocument` (read through the cache) with `InvalidOperationException`. CultMesh's single-file document helpers (`CultMesh.WriteSingleFileDocument`,
|
||||
same private write) with `InvalidOperationException`. CultMesh's single-file document helpers (`CultMesh.WriteSingleFileDocument`,
|
||||
`ReadSingleFileDocument` and their payload forms) are built on these members and nothing else of the store.
|
||||
|
||||
## Runtime status
|
||||
|
|
|
|||
|
|
@ -14,17 +14,23 @@ Releases as 3.0.0 (major).
|
|||
- Writes are write sets. A flush or commit replaces the records it staged and copies every other record forward as stored, under the store's lock, instead of rewriting the whole store from the cache's memory. A record the cache does not know is kept, and an untouched record keeps a dropped member's slot through every write (never reuse a dropped slot; see the schema-compatibility contract).
|
||||
- A write or removal at a key whose stored record is under a schema id the writer does not declare is refused with `CultSchemaConflictException`, and the file is left as it was. A cache declares through its registry; a cacheless CultMesh write declares through the catalog entry it writes.
|
||||
- Registration refuses an owned object-list element type that has no `[CultElementId]` member (a public string with `[Key]`), naming the type and the list. Element ids let a write set merge a list by identity; a list of record references or plain values is a whole value and is not affected.
|
||||
- `SingleFileBackingStore`'s abstract and protected surface changed for store kinds outside this library: `SerializeSnapshot` and `DeserializeSnapshot` are replaced by `SerializeStore`, `SerializeRecord`, `SerializeCatalogEntry` and `ReadStore`, and `HeaderFor`, `HeaderForWrite`, `Foreign`, `SetForeignRecords`, `RefuseUndeclared`, `RefuseUndeclaredOnDisk`, `StagedChecks`, `StageChecks`, `JudgeChecks` and `RefuseRemovalOfUndeclared` are new protected members a store kind implements or calls. `MarkFlushSucceeded` and the `Judging` hook are removed.
|
||||
- The abstract and protected surface for store kinds outside this library changed. On `SingleFileBackingStore`, `SerializeSnapshot` and `DeserializeSnapshot` are replaced by the abstract `SerializeStore`, `SerializeRecord`, `SerializeCatalogEntry` and `ReadStore`, and the abstract `SerializePayload(object)` becomes `SerializePayload(object document, Type documentType)`: a store encodes a document as the type it is given. On `CacheBackingStore`, `Judging` is removed, `ToStoredDocument` takes the record's version (`record, version, catalog, deserialize, out report`), and these protected members are new for a store kind to implement or call: `HeaderFor`, `HeaderForWrite`, `Foreign`, `ForeignRecords`, `SetForeignRecords`, `Undeclared`, `RefuseUndeclared`, `RefuseUndeclaredOnDisk`, `RefuseRemovalOfUndeclared`, `Refuse`, `SameStoredIdentity`, `VersionOf`, `StoredAtOver`, `Overwrites`, `StagedChecks`, `StageChecks` and `JudgeChecks`. `MarkFlushSucceeded` is unchanged.
|
||||
- `CultCommitRequest.Expected` is now `IReadOnlyList<(CultRecordKey Key, string? Version)>` (it was a key with a schema id and a stored-at), `CultCommitRequest.Checks` is new, and `ConditionsHold` takes `(IReadOnlyDictionary<string, string> durable, IEnumerable<CultStoredDocument> observed, IEnumerable<CultForeignRecord> foreign)` in place of `(IReadOnlyCollection<CultPersistedRecord>, IEnumerable<CultStoredDocument>)`. A store kind outside the library lists the record versions it holds and passes its foreign records.
|
||||
- `GameCult.Mesh`: `AsSchemaAlias<TAlias>()` is removed from `CultMeshDocumentHandle` and `CultMeshCollectionHandle`. Read the handle by the schema you mean.
|
||||
- The element types of owned lists in `GameCult.Mesh` (`CultMeshBodies`, `CultMeshCdn`, `CultMeshDiscoveryService` and `CultMeshEntityPrefabs`) and `GameCult.Networking` (`CultWitnessArtifactBundle`) gain a public string `Id` member with `[CultElementId]`, so their lists merge by identity. Code that matched those elements positionally by shape must set or read `Id`.
|
||||
- The element types of owned lists gain a public string `Id` member with `[CultElementId]`, so their lists merge by identity: in `GameCult.Mesh`, `CultMeshBodyDescriptor`, `CultMeshCdnChunkRef`, `CultMeshDiscoveryCandidateDocument`, `CultMeshEntityPrefabComponent`, `CultMeshEntityPrefabAssetRef` and `CultMeshEntityPrefabNode`; in `GameCult.Networking`, `CultWitnessContractPin`, `CultWitnessArtifactEntry` and `CultWitnessTimingEntry`; in `GameCult.Geometry`, `CultGeometryDomainNode`, `CultGeometryFeatureClaim` and `CultGeometryContributionRow`. Code that matched those elements positionally by shape must set or read `Id`.
|
||||
- `CultNetDocumentRegistry.Register` refuses a binding that claims a document type or a schema id another binding already holds, with `CultSchemaConflictException`; binding a type to the schema id it already holds still replaces that binding.
|
||||
- `InternalsVisibleTo GameCult.Mesh` is removed from `GameCult.Caching`: CultMesh's single-file document helpers use the public `ReadStoreSnapshot`, `WriteDocument`, `DecodeDocument` and `WriteRecord` below.
|
||||
- `InternalsVisibleTo GameCult.Mesh` is removed from `GameCult.Caching`: CultMesh's single-file document helpers use the public `ReadStoreSnapshot`, `WriteDocument`, `WriteRecord` and `CultCodec.DecodeDocument` below.
|
||||
|
||||
### Added
|
||||
|
||||
- `SingleFileBackingStore.ReadStoreSnapshot()` reads a store file once, under the store's lock, 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: 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 and marks the store when it holds an id; a failed write gives the minted ids back. `SingleFileBackingStore.DecodeDocument<T>(key, payload, registry)` decodes a stored payload as a cache load does. Both refuse on a store attached to a cache. `CultElementIds` stays internal.
|
||||
- `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.
|
||||
- `CultPersistedStoreSnapshot.FormatV3` names the element-id store header.
|
||||
- `CultDocumentAttribute.CompatibleSchemaIds` and `CultDocumentDescriptor.CompatibleSchemaIds`: the schema ids besides its own a document type claims, so a record stored under one of them loads as that type.
|
||||
- `CultStoredDocument.StoredSchemaId` (the schema id the record carries in its store, which may be an older one) and `CultStoredDocument.HoldsIds`.
|
||||
- `CultCache.MintElementIds()` rewrites, once, the records of a store written before element ids whose ids exist only in memory, and returns how many it rewrote.
|
||||
- `CultElementIdAttribute` declares an element id (derived from a named member when given), `CultForeignRecord` is a record a store carries untouched because no registered type reads it, and `CultElementIdException`, `CultSchemaConflictException`, `CultStoreUnreadableException` and `CultWriteConflictException` are the refusals named above.
|
||||
- A store written by 1.0.x with a record-reference list in a slot still opens (from 2.0.1).
|
||||
|
||||
## [2.0.1] - 2026-10-22
|
||||
|
|
|
|||
Loading…
Reference in a new issue