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 connection to the server was interrupted. Trying to restore it…
Trying again…
The connection could not be restored. Reloading the page…
The server was updated. Reloading the page to pick up the latest version.