Commit graph CultLib/tests/GameCult.Mesh.Tests/CultMeshStreamingTests.cs
Author SHA1 Message Date
Meta Vens
7c4bc2bdc0 Make generated and reflective document members one rule
The generator and CultDocumentRegistry now discover members by the same
steps: most-derived declaration first, override chain collapsed to one
member, attributes read up the override chain, rejections in one order
with one text (GCC001 / InvalidOperationException).

- Accessors are named per document, so documents sharing a [CultName] or
  [CultIndex] base member no longer emit duplicate methods (CS0111).
- Duplicate [Key] slots are refused, including new-hidden fields and
  properties; a hidden persisted member with a different slot is refused.
- String [Key("x")] gets its own message instead of "has no [Key]".
- A persisted property whose set accessor is private, protected or init,
  or a readonly field, is refused instead of CS0272 in generated code.
- Overrides: MessagePack's DynamicObjectResolver reads the base
  declaration's [Key]/[IgnoreMember] (it throws "key is duplicated" for a
  re-keyed override and serializes an override marked [IgnoreMember]).
  An override that changes them is refused in both paths; an override
  that repeats or omits them is accepted and byte-pinned.
- Typed Serialize<T>/Deserialize<T> used static Options and skipped
  generated codecs and declared resolvers; deleted. CultNetDocumentBinding
  and CultMeshSnapshots route through the untyped path with their registry.

DocumentShapeSweepTests compiles each shape through the generator and
compares generated vs reflective schema id, catalog, name/index values
and payload against MessagePack's own bytes, or equal rejection text.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 22:00:16 +02:00
Meta Vens
825b4f7d26 Mirror snapshots carry the cache Sequence they reflect
ObserveAsync, ReactiveAsync and RefreshAsync read a snapshot without a
Sequence, so an older change admitted before the read but delivered after it
overwrote the newer value.

CultCache.GetWithSequence(key) returns the document and the cache's Sequence,
read together under the gate. A cache-backed CultMesh handle passes it to its
mirrors; each mirror subscribes first, then adopts the sequenced read under its
own gate and takes that Sequence as applied, so a straggler at or below it
drops. RefreshAsync on both mirrors uses the same adoption path. Public Mesh
surface unchanged. The read is non-generic because the Mesh route needs the
untyped document for same-schema conversion.

Contract: only Watch/WatchRecord changes carry Sequence; OnUpdate-derived
CultNet streams have no ordering or stale protection; schema-alias handles and
removals are not stale-protected; the sequence constructor argument changed
CultCacheDocumentChange<T>'s binary signature.

Tests: ObserveAfterNewerSnapshotIgnoresStraggler and
RefreshIgnoresStragglerOlderThanSnapshot (observed and reactive cases) fail on
4562340; SequencedReadReflectsEveryChangeAtOrBelowItsSequence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 21:02:49 +02:00
Meta Vens
456234018f Carry order as data: per-cache change Sequence, no delivery scheduler
bcae483 made the cache a delivery scheduler (tickets, _deliveryGate turn
waits, per-thread delivery queues). It deadlocked inside CultLib: a load's
delivery reaching a CultNetDatabaseSubscriptionServer observer that needs
_lifecycleGate, while a subscribe's demand handler holding that gate writes
the cache and waits for the delivery's turn.

The cache now owns order as data. Every admitted change takes the next value
of a per-cache in-memory sequence under the gate, published as
CultCacheDocumentChange<T>.Sequence. Delivery is the 544c087/b653f68 rule:
each call publishes its own changes on its own thread after releasing the
gate; nothing waits on another call's delivery. Cross-thread delivery order is
not guaranteed.

Deleted: ticket counter, _delivered, _deliveryGate, Monitor.Wait turns,
per-thread delivery queues, per-ticket catch-all, and the save-order contract
text with its observer-must-not-block rule.

Consumer: a cache-backed CultMesh document handle hands its observed and
reactive mirrors each snapshot's Sequence; ApplyCanonicalSnapshot drops one
not newer than the last applied.

Tests: SequenceIncreasesInAdmissionOrder,
ObserverWritingTheSameCacheDoesNotWaitOnOtherDeliveries,
SubscriptionDemandHandlerWritingCacheDuringDeliveryDoesNotDeadlock,
WatchRecordIgnoresStaleSequence. The last three time out on bcae483.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 20:43:11 +02:00
Meta Vens
a3cc394241 Route each document type to one home store; attach hydrates; commit explicit batches
Cut 3 of the CultCache migration (Aetheria docs/cultcache-migration-cut.md, 2.1
and 2.2). One commit, because routing, the batch that replaces the ambient
transaction, and conditional commit share one admission path.

Deleted first:
- globals: InitializeGlobals, MaterializeMissingGlobals, the two-argument
  constructor, the initializeGlobals plumbing in CultCacheMessagePack,
  ContainsDurableRecord (cache and directory store)
- replication: the push-to-every-store loops and the attach-time push of
  existing records
- typeof(T) as the schema owner in AddAsync/UpsertAsync and in
  CultNetDatabase.PutAsync/PutPredictedAsync
- the ambient transaction: ExecuteTransactionAsync x2, CultCacheTransaction,
  CommitTransaction, VisibleStoredDocuments, the AsyncLocal, the SemaphoreSlim
  gate, and every ambient check
- CultCacheOpenOptions.PullOnOpen; OpenAsync is Task.FromResult(Create(...))
- DirectoryMessagePackBackingStore.ResolveLegacyUncataloguedRecordCatalog: the
  catalog precommit (fe55bff) is an ancestor of the v4 page format (eea8248),
  so no v4 manifest can reach it
- CultCache.DeleteAsync<T>; its one caller, CultNetDatabaseSubscriptionClient,
  called it by reflection and now calls Remove(key)

Added:
- AddBackingStore(store, params Type[] homes): attach validates (clean, not
  already attached, one untyped store, no claimed home type, no admitted
  record changes home) and then pulls under the gate
- Home(type): most derived routed home, else the untyped store
- Admit: every add and remove, from a write, a batch or a load, validates
  (home, read-only, loaded-from-home, global singleton), lands durably, then
  applies and publishes; a pull hands its whole delta over in one call, so a
  refused record leaves the store and the cache unchanged
- read-only stores (CacheBackingStore(readOnly), both store constructors,
  CultCacheOpenOptions.ReadOnly); flush skips them and writes only dirty stores
- assignable GetGlobal<T>/GetByName<T>/GetByIndex<T>/Watch<T>; more than one
  candidate throws naming them
- Commit(Action<CultCacheBatch>) returning bool, TryCommit returning
  CultCommitOutcome, CultCacheBatch (Upsert, Remove, Expect, ExpectUnchanged,
  sealed after commit), CultCommitRequest; CacheBackingStore.CommitBatch
  evaluates conditions under the store's exclusive lock
- single-file store: <path>.lock sidecar for CommitBatch and PushAll; a clean
  store commits onto the file as it is, so records it never loaded survive
- storedAt minted in one place and bumped a tick when not later than the record
  it replaces

Consumer adjustments in this repo: Mesh streaming tests drop PullOnOpen; the
Studio bridge stops reflecting on it; BackingStoreTests' global test creates
its global explicitly and its transaction tests are rewritten against Commit
(the two that proved ambient hydration rules are deleted); the pre-Cut 2
refusal test expects the attach to throw.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 17:57:58 +02:00
Meta Vens
fe7e5fbe37 Delete the parked CultCache SoA table
Removes CultSoaTable, CultSoaColumn, CultCacheSoaStore, CultCacheSoaTypeTable,
CultCacheSoaMember, CultCache.Soa<T>, the _soa field and its four hooks, and
the two tests that exercised them (with their TransformEntry and
MeshManagedPlayer fixtures). The last working form is at
parked/cultcache-soa.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 16:22:54 +02:00
Meta Vens
4f6e95bb3d Carry editable field authority in CultMesh bindings 2026-08-22 00:05:57 +02:00
Meta Vens
ba680020b9 Prove reactive scheduling scales with edits 2026-08-18 00:44:41 +02:00
Meta Vens
98bec47d75 Make CultMesh document writes authority-explicit 2026-08-17 19:47:10 +02:00
Meta Vens
3e223ceecd Negotiate exact hot-body transport demand 2026-07-19 03:17:11 +01:00
Meta Vens
581c5d0249 Read legacy CultMesh route records 2026-07-13 16:53:22 +01:00
Meta Vens
0a092d8c7a Own CultMesh SoA frame publication 2026-07-13 15:49:53 +01:00
Meta Vens
455dcd8f9e Report CultNet failures during connection 2026-07-12 10:38:45 +01:00
Meta Vens
6be0573ffc Surface CultNet background transport failures 2026-07-12 10:33:02 +01:00
Meta Vens
905c5254b4 Reuse CultMesh snapshot connections 2026-07-10 06:37:10 +01:00
Meta Vens
a769895e13 Add C# reactive document edge coverage 2026-06-28 00:04:48 +01:00
Meta Vens
e94cd0973d Add C# publication catalog sync 2026-06-27 23:53:35 +01:00
Meta Vens
47a84f5689 Add C# publication document catalogs 2026-06-27 23:48:53 +01:00
Meta Vens
abdfff6f4e Add publication sync and reconciliation coverage 2026-06-27 23:37:15 +01:00
Meta Vens
76a38ec36e Add C# typed peer snapshot node sync 2026-06-27 23:18:48 +01:00
Meta Vens
672f59dba0 Prefer direct alias remote snapshot handles 2026-06-27 22:53:45 +01:00
Meta Vens
964a3ff1f8 Add schema-aware collection catalogs 2026-06-27 22:33:50 +01:00
Meta Vens
6218900734 Prefer typed peer snapshot aliases by record key 2026-06-27 22:25:15 +01:00
Meta Vens
26159f9cfa Index C# document catalogs by schema id 2026-06-27 22:16:26 +01:00
Meta Vens
e430a725ad Apply snapshot aliases by payload schema 2026-06-27 21:12:05 +01:00
Meta Vens
d5f9d2d752 Request snapshot aliases by record key 2026-06-27 21:03:35 +01:00
Meta Vens
b16f49665a Decode snapshot aliases from compatible payloads 2026-06-27 21:00:39 +01:00
Meta Vens
8cc147d367 Add node-level reactive document shortcuts 2026-06-27 20:35:59 +01:00
Meta Vens
8a88ae547d Expand CultMesh reactive sync edge tests 2026-06-27 20:05:56 +01:00
Meta Vens
e3e07dfc58 Add CultMesh reactive reconciliation deltas 2026-06-27 19:56:08 +01:00
Meta Vens
8cdfdb656c Detect direct edits in CultMesh reactive documents 2026-06-27 19:50:30 +01:00
Meta Vens
5887134193 Expand CultMesh cross-runtime reactive tests 2026-06-27 19:31:59 +01:00
Meta Vens
ce7a5b2158 Expand CultMesh reactive document tests 2026-06-27 19:25:10 +01:00
Meta Vens
ccd5a0e9cf Add managed CultMesh reactive documents 2026-06-27 19:19:09 +01:00
Meta Vens
1758a9c52f Add transparent CultMesh document mutation 2026-06-27 18:58:19 +01:00
Meta Vens
07d4c4164a Add synced snapshot endpoint facade 2026-06-27 18:37:56 +01:00
Meta Vens
c8fd765aef Add synced snapshot endpoint handles 2026-06-27 18:34:45 +01:00
Meta Vens
3720a8d39d Add snapshot endpoint document bindings 2026-06-27 18:30:17 +01:00
Meta Vens
7bb6d7da8c Add CultMesh multi-document latest helpers 2026-06-27 18:23:26 +01:00
Meta Vens
c45a11e2c8 Add CultMesh document registry helpers 2026-06-27 18:01:12 +01:00
Meta Vens
39e13fdb3c Add typed snapshot sync helpers 2026-06-27 17:50:02 +01:00
Meta Vens
8b48aa512f Add typed snapshot endpoint surface 2026-06-27 17:45:18 +01:00
Meta Vens
a6172aa586 Add CultMesh scoped snapshot helpers 2026-06-27 17:36:35 +01:00
Meta Vens
04561a5bd9 Add CultMesh single-file document helpers 2026-06-27 17:28:15 +01:00
Meta Vens
bcf877d87d Add C# configured publication document handles 2026-06-27 17:10:52 +01:00
Meta Vens
a6537a2d93 Add C# store document handles 2026-06-27 17:03:10 +01:00
Meta Vens
bd095a5837 Request peer snapshot documents by record key 2026-06-27 16:40:42 +01:00
Meta Vens
a57fa1113a Add C# peer snapshot document handles 2026-06-27 16:37:44 +01:00
Meta Vens
52ff632d70 Add CultMesh document catalog mutations 2026-06-27 16:28:42 +01:00
Meta Vens
a08a66a66e Add CultMesh document prediction submissions 2026-06-27 16:20:19 +01:00
Meta Vens
b6d4ebc920 Add synchronous CultMesh document reads 2026-06-27 16:03:10 +01:00