Table of Contents

Versioned

Full MVCC snapshot isolation and zero-loss durability for data that can never be lost or read torn.

Status: โœ… Implemented ยท Visibility: Public ยท Level: ๐ŸŸข Start Here ยท Category: Ecs

๐ŸŽฏ What it solves

Some component data โ€” inventory, currency, guild membership, progression โ€” must never be lost on crash, must never be visible to a concurrent transaction in a half-written state, and sometimes needs to be read "as of" a prior point in time. Versioned is Typhon's default storage mode: every write is isolated until commit, every commit is WAL-durable, and concurrent readers always see a consistent value.

โš™๏ธ How it works (in brief)

A Versioned component keeps a per-entity revision chain alongside its data. EntityRef.Write<T>() is copy-on-write โ€” it allocates a new revision and copies forward rather than overwriting in place, so concurrent readers keep seeing the prior committed value until your transaction commits. Every transaction is WAL-logged; on commit the new revision becomes the visible HEAD and is durable. Reads walk the chain under transaction- sequence-number visibility, giving each transaction a consistent snapshot of the data.

๐Ÿ’ป Usage

[Component("Game.Inventory", 1)]   // StorageMode.Versioned is the default โ€” no need to set it explicitly
public struct Inventory
{
    [Index] public int ItemId;
    public int Quantity;
}

[Archetype]
partial class Player : Archetype<Player>
{
    public static readonly Comp<Inventory> Inventory = Register<Inventory>();
}

using var tx = dbe.CreateQuickTransaction();
var id = tx.Spawn<Player>(Player.Inventory.Set(new Inventory { ItemId = 7, Quantity = 1 }));
tx.Commit();

using var tx2 = dbe.CreateQuickTransaction();
var e = tx2.OpenMut(id);
ref var inv = ref e.Write(Player.Inventory);
inv.Quantity += 1;            // copy-on-write โ€” old revision still visible to concurrent readers
tx2.Commit();                 // new revision becomes HEAD, durable

โš ๏ธ Guarantees & limits

  • Zero data loss on crash โ€” every committed write is WAL-durable and recoverable to the exact pre-crash state.
  • Full snapshot isolation: concurrent transactions never see another transaction's uncommitted writes; conflicting writes are detected at commit.
  • tx.Rollback() discards the staged revision entirely โ€” the prior committed value is untouched.
  • The most expensive mode: ~250 ns/write versus ~40 ns for SingleVersion/Transient โ€” ~6ร— (copy-on-write allocation, revision-chain append, eventual chain GC).
  • Carries per-entity chain memory overhead even when a component is rarely written.
  • Required for ReadsSnapshot / temporal "AS OF" reads and TAIL (committed-value) indexes โ€” these guarantees are Versioned-only; SingleVersion and Transient reject them.

๐Ÿงช Tests

  • EcsSpawnMvccTests โ€” copy-on-write on Write, concurrent transaction still seeing old data, rollback freeing the new chunk, revision-chain creation
  • Sibling: Committed Durability Discipline โ€” gets Versioned-grade commit atomicity on the cheaper SingleVersion layout, for writes that don't need snapshot isolation
  • Sibling: Revision โ€” the MVCC revision-chain subsystem Versioned writes append into
  • Parent feature: Storage Modes