WAN active-active geo-replication
Clustron Zaris can replicate a store between independent clusters in different datacenters over a resumable, pull‑based change feed. Each site is a complete, self‑coordinating Zaris cluster — not a stretched cluster — so every site runs at full local speed, serves in‑region reads and writes, and a WAN partition degrades replication lag, never local availability. When the link returns, each site resumes from its durable cursor.
Geo‑replication is new in Zaris 2.1.0, off by default, and additive: a store with no site configuration behaves exactly as a normal single‑site store.
The model
- Site — one independent Zaris cluster, with its own membership, partition map, and store, identified by a site id.
- Change feed — the per‑segment, revision‑ordered stream of a store's mutations. A peer pulls this feed and applies it locally. The feed is gated at the replicated watermark, so a consumer never sees a write that an owner crash could still lose.
- Cursor —
(generation, revision)per segment: a site's durable position in a peer's feed. After a restart or a WAN cut, the puller resumes from here — no full resync. - Conflict version — a hybrid logical clock
(physicalMs, logical)plus the origin site id, stamped once on the original write. Last‑writer‑wins compares these (physical, then logical, then site id). - Origin — the site that first wrote a value. A feed serves only its own‑origin changes, so nothing echoes back around a two‑way topology.
Active-passive vs active-active
Active → passive — one site is the active primary; the peer is a read‑only mirror that trails it over the feed. The passive refuses application writes (a non‑retryable "write elsewhere" error) but serves reads and watches from its local apply. A WAN cut just raises lag; on restore the mirror catches up.
Active-active — every site is active and lists every other site as a peer. Each site pulls every other site's feed, and because a feed serves only own‑origin changes, writes never ping‑pong. Every site takes writes locally and all sites converge.
Conflict resolution
When two sites write the same key concurrently, resolution is deterministic and symmetric — every site computes the same winner for the same pair of writes:
- Scalar values use hybrid‑logical‑clock last‑writer‑wins. The higher conflict version wins; the losing write is discarded. Use counters where every update must survive.
- Counters use a PN‑counter: each site writes only its own component and the value is the sum, so concurrent increments at different sites are never lost.
- Collections (hash / list / set / sorted‑set / stream) are whole‑value last‑writer‑wins in this release — a concurrent change replaces the whole value by version, with no field‑level merge.
- A delete is a versioned tombstone decided by the same rule; a newer delete beats an older put and vice‑versa. A clear is geo‑aware: it replicates to peers as versioned tombstones rather than silently dropping only local data.
A site‑apply fires a local watch event only when the incoming change wins — a losing or re‑delivered change raises no event.
What stays site-local
CAS, transactions, locks and leases are site‑local: they are not
coordinated across sites, so do not rely on them for cross‑site mutual exclusion. TTL crosses as an
absolute expiry — an item expires at the same wall‑clock instant on every site and does not restart on
apply or resync.
Setup
A store becomes a site by adding a site section to its configuration (additive; absent = a normal
single‑site store):
site {
id: "dc1" # this cluster's site id
role: Active | Passive # Passive refuses app writes (reads/watches allowed)
epoch: <long> # monotonic site epoch, minted by the management plane
peers: [ # every peer this site replicates with
{ id: "dc2", store: "orders", seeds: "dc2-a:7861,dc2-b:7861" }
]
mergePolicy: LastWriterWins # the only policy in this release
homeSitePrefixes: [ "us/" ] # optional: keys under these prefixes are homed here
}
Environment overrides (primarily for tests/ops): ZARIS_WAN_SYNC_FEED=1 enables the feed on a cluster;
ZARIS_WAN_SYNC_FEED_RETENTION_OPS / ZARIS_WAN_SYNC_FEED_RETENTION_BYTES size the resumable tail.
With the PowerShell cmdlets
The supported, operator‑facing path configures both sides in one call with the Zr AdminShell
cmdlets — it POSTs the site/change‑feed config to the local and the peer managers and enables the feed:
# Link two stores active-active (bidirectional, last-writer-wins)
New-ZrSiteLink -Store orders-dc1 `
-PeerWorkspace prod-dc2 -PeerStore orders-dc2 `
-LocalSeeds 20.59.9.10:7863 -PeerSeeds 20.59.9.241:7863 `
-Mode ActiveActive -MergePolicy LastWriterWins -Restart
Get-ZrSiteStatus -Store orders-dc1 # configured state + per-peer, per-segment lag
Remove-ZrSiteLink -Store orders-dc1 -PeerStore orders-dc2 -PeerWorkspace prod-dc2 -Restart
For an active‑passive link, use -Mode ActivePassive -Primary <store>. The
WanReplication.Setup sample
is a fully‑commented script that does this end to end.
Switch-over / failover. The epoch fence and role are implemented and persisted, but the orchestrated switch‑over (
Switch‑ZrActiveSite) and active‑active evacuate (Suspend‑ZrSite/Resume‑ZrSite) are a documented roadmap item — not shipped cmdlets in this release. A planned switch‑over proceeds with zero loss only when the target's lag is0; a forced switch‑over accepts and reports a bounded tail‑loss.
Observability
site.*counters, both directions — applied, skipped, conflicts, and bytes, with live rates. Conflict counts are approximate (they count "an incoming change was based on a different version than the one held") and never influence resolution.- RPO is reported as the age of the oldest un‑applied op — about zero when caught up, climbing to real backlog‑seconds when a site falls behind.
- Per‑peer, per‑segment lag (revisions and seconds) is available from
Get-ZrSiteStatusand gates a planned switch‑over's zero‑loss check. - The web console renders an animated, live replication topology (active vs passive sites, one‑way vs bidirectional lanes, per‑site item counts) with a health banner (healthy / falling‑behind / stalled) and a dashboard callout.
Clock requirements
Last‑writer‑wins and TTL use wall‑clock‑seeded hybrid logical clocks, so keep site clocks synchronized (NTP). A change stamped too far into the future is rejected/quarantined by the clock‑skew guard and the skew is exposed as a metric — investigate skew alerts promptly, as sustained skew distorts LWW outcomes and TTL.
Guarantees and limits
- Eventual consistency. A read reflects the local site's state; sites converge as the feed propagates. There is no cross‑site read‑your‑write or linearizability.
- Last‑writer‑wins loses the losing write for a scalar key. Use counters (PN‑counter) where every update must survive.
- Collections are whole‑value LWW in this release (no field‑level merge).
- Forced switch‑over accepts and reports a bounded tail‑loss (writes not yet replicated when the old active was lost).
Operational notes
- Off by default and additive — enable it per store; a store with no site config is unchanged.
- Validated on a two‑region AKS deployment over private network peering, including a three‑store isolation run.
- Geo‑replication is built directly on Change Data Capture — the same durable, resumable change feed is the transport.
Related
- Change Data Capture — the change feed that replication rides on.
- Scaling and high availability — scaling and failover within a single site.