MeshWeaver takes a pragmatic stance on data versioning: rather than imposing a single strategy across all backends, it delegates to each store's native capabilities wherever they exist. The result is richer history, lower application complexity, and better performance than any cross-cutting shim could deliver.

Scope — read this first. This page is about historical versions of the data a NodeType holds (a pricing table, a claim, a contract) in whatever store backs it, and it is largely a guide to the backends' own mechanisms — the code samples below are the shape you would implement in a storage adapter, not a framework API you can call. There is no VersionedEntityReference and no @V{n} path resolution in the framework today. For the live MeshNode graph's revision counter — which is fully implemented — see MeshNode Versioning; for a node's edit history and restore, see the version tooling on the node itself (get_versions / restore_version). Versioning Strategy by Backend Snowflake Time Travel Retention: 1–90 days Zero-copy clones AT(TIMESTAMP => …) Fail-safe 7 days ✦ Native — zero app code required SQL Server Temporal Tables Retention: unlimited Row-level history FOR SYSTEM_TIME AS OF Auto on UPDATE/DELETE ✦ Native — zero app code required Cosmos DB Manual Versioning Retention: unlimited Explicit snapshots path@V pattern Version in document id ✦ App-managed — explicit SaveVersion Blob Storage Manual / Native Configurable retention Folder-per-entity current.json pointer Azure blob versioning ✦ App-managed or storage-native

Four versioning strategies — native time-travel on the left, explicit path-based snapshots on the right.

Choosing a Strategy

Your data store determines the versioning model:

flowchart TB Q{Data Store?} Q -->|Snowflake| SF[Time Travel<br/>Up to 90 days] Q -->|SQL Server| SQL[Temporal Tables<br/>System-versioned] Q -->|Cosmos DB| CD[Manual Versioning<br/>path@V1, V2] Q -->|File/Blob| FB[Manual Versioning<br/>path@V1, V2]
Technology Method Retention Query Syntax
Snowflake Time Travel 1–90 days AT(TIMESTAMP => ...)
SQL Server Temporal Tables Unlimited FOR SYSTEM_TIME AS OF
Cosmos DB Manual Unlimited path@V{n}
Blob Storage Manual / Native Configurable Folder or blob versioning

Snowflake: Time Travel

Snowflake's Time Travel gives you transparent, zero-effort history for any table — no triggers, no shadow tables, no ETL.

Capabilities

Feature Description
Time Travel Query data as it existed at any point, up to 90 days in the past
Fail-safe A 7-day recovery window after the Time Travel period expires
Zero-Copy Cloning Instant snapshots with no data duplication
Retention Configurable per table from 1 to 90 days

Querying Historical Data

-- Data as it was 1 hour ago
SELECT * FROM pricing
AT(OFFSET => -3600);

-- Data at a specific timestamp
SELECT * FROM pricing
AT(TIMESTAMP => '2024-01-15 10:00:00');

-- Data as it existed before a specific statement ran
SELECT * FROM pricing
BEFORE(STATEMENT => '01234567-89ab-cdef-0123-456789abcdef');

Cloning for Snapshots

Zero-copy clones let you take an instant point-in-time snapshot without consuming additional storage:

CREATE TABLE pricing_q4_snapshot
CLONE pricing
AT(TIMESTAMP => '2024-12-31 23:59:59');

Reaching it from MeshWeaver

There is no built-in time-travel workspace referenceVersionedEntityReference does not exist. A NodeType that wants "as of" reads over a Time-Travel-capable store adds it the ordinary way: an AT(TIMESTAMP => …) clause in the query its own data source issues, exposed to callers as a normal reactive read (an asOf parameter on the type's own request/observable). Keep the AT(...) inside the storage leaf — the leaf is where the async I/O lives, and it goes through IIoPool like every other I/O edge (Controlled I/O Pooling).


SQL Server: Temporal Tables

SQL Server's system-versioned temporal tables automatically maintain a complete row-level history with no application-layer changes required after the initial schema setup.

Creating a Temporal Table

CREATE TABLE Contracts
(
    Id INT PRIMARY KEY,
    Name NVARCHAR(100),
    Amount DECIMAL(18,2),
    ValidFrom DATETIME2 GENERATED ALWAYS AS ROW START,
    ValidTo   DATETIME2 GENERATED ALWAYS AS ROW END,
    PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo)
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.ContractsHistory));

Querying Historical Data

-- Current data (unchanged syntax)
SELECT * FROM Contracts;

-- State at a specific point in time
SELECT * FROM Contracts
FOR SYSTEM_TIME AS OF '2024-06-01 12:00:00';

-- All rows that were active within a date range
SELECT * FROM Contracts
FOR SYSTEM_TIME BETWEEN '2024-01-01' AND '2024-12-31';

-- Complete change history
SELECT * FROM Contracts
FOR SYSTEM_TIME ALL;

Why Temporal Tables Work Well Here


Manual Versioning: Path Pattern

When the underlying store has no built-in history mechanism (Cosmos DB, Azure Blob Storage), the recommended convention is explicit path-based versioning. This is a convention for an adapter you write — nothing in the framework parses or resolves @V{n} today.

flowchart LR P[pricing/MS-2024] --> V1[pricing/MS-2024@V1] P --> V2[pricing/MS-2024@V2] P --> VN[pricing/MS-2024@V3] style P fill:#e1f5fe style V1 fill:#f5f5f5 style V2 fill:#f5f5f5 style VN fill:#c8e6c9

Path Format

{path}@V{version}

The undecorated path always points to the current version; decorated paths are immutable snapshots:

Path Meaning
pricing/MS-2024 Current version
pricing/MS-2024@V1 Version 1 (immutable)
pricing/MS-2024@V2 Version 2 (immutable)
contracts/deal-123@V5 Version 5 of deal-123

Implementation Pattern

🚨 The snippet below is the storage-leaf shape — the innermost Task-returning methods that actually talk to Cosmos/blob. It must not surface as a Task API: the adapter's public surface returns IObservable<T> and bridges these leaves through IIoPool (pool.Invoke(ct => SaveVersionAsync(path, data, ct))), never Observable.FromAsync, and no hub-reachable or Blazor code ever awaits them. See Asynchronous Calls and Controlled I/O Pooling.

// Storage leaf — save a new version
private async Task SaveVersionAsync(string path, object data, CancellationToken ct)
{
    // Determine the next version number
    var current = await GetCurrentVersionAsync(path, ct);
    var newVersion = current + 1;

    // Write the immutable versioned snapshot
    await SaveAsync($"{path}@V{newVersion}", data, ct);

    // Advance the current pointer
    await SaveAsync(path, data, ct);
}

// Public surface — reactive, pooled
public IObservable<Unit> SaveVersion(string path, object data) =>
    ioPool.Invoke(ct => SaveVersionAsync(path, data, ct));

public IObservable<T> GetVersion<T>(string path, int version) =>
    ioPool.Invoke(ct => GetAsync<T>($"{path}@V{version}", ct));

Cosmos DB

Include the version in the document itself (and optionally in the partition key):

{
  "id": "MS-2024@V3",
  "partitionKey": "pricing",
  "version": 3,
  "createdAt": "2024-03-15T10:00:00Z",
  "data": { }
}

Blob Storage

Use a folder-per-entity layout with a current.json pointer, or enable Azure Blob versioning to get automatic version tracking at the storage layer:

pricing/
  MS-2024/
    current.json        ← always the latest version
    v1.json
    v2.json
    v3.json

Version Metadata

Regardless of which storage technology is in use, each version snapshot should carry consistent metadata so the audit trail is human-readable:

{
  "version": 3,
  "createdAt": "2024-03-15T10:00:00Z",
  "createdBy": "user@example.com",
  "comment": "Updated Q1 projections",
  "previousVersion": 2
}

Best Practices

  1. Prefer native features. Snowflake Time Travel and SQL Server temporal tables provide history at zero application cost — use them before reaching for manual versioning.
  2. Use consistent naming. Manual versions always use the @V{n} suffix so path-parsing code has a single, unambiguous pattern to follow.
  3. Always record metadata. Capture who created the version, when, and why — debugging and auditing are far easier with this context.
  4. Set retention policies. Native Time Travel and blob versioning can accumulate storage costs; configure table-level retention in Snowflake and lifecycle rules in Azure Storage accordingly.
  5. Lean on point-in-time queries. Reconstructing state as of a specific timestamp is the cleanest way to answer audit questions — avoid reconstructing it manually from event logs when the store offers AS OF semantics natively.
Reconnecting…
The server was updated. Reloading the page to pick up the latest version.