PATTERN Cited by 1 source
Eliminate partial updates¶
Problem¶
In a CRDT-replicated state-distribution system, producers face a choice for how to publish changes:
- Delta republish — send only the fields that changed. Compact over the wire; easy to reason about at the producer; but produces a stream of small messages each of which must be correctly merged.
- Whole-object republish — send the entire object every time any part of it changes. More bytes over the wire; but the merge-layer's conflict resolution does the filtering work for free.
Delta republish looks cheaper, but in a gossiped CRDT it creates a class of subtle bugs: partial updates can arrive out of order, be lost in gossip, combine with older updates in ways that produce invalid composite states, or expose the inner ordering of concurrent changes.
Pattern¶
Always publish whole objects — never partial updates — into a CRDT-replicated shared state. Let the CRDT layer's [[concepts/last-write-wins|last-write-wins] merge filter out no-op changes.
Why it works:
- CRDTs filter no-ops at the merge layer. cr-sqlite compares incoming values against local values; identical values produce no log entry and no gossip traffic.
- Whole-object publish is idempotent. Re-publishing the same object twice produces no change; delta publishes require careful sequencing.
- Forecloses a bug class — no more reasoning about cross-field consistency of individual deltas; the publishing contract is just "the object currently looks like this."
Canonical wiki instance — Fly.io Corrosion (2025)¶
From sources/2025-10-22-flyio-corrosion:
"Until recently, any time a worker updated its local database, we published the same incremental update to Corrosion. But now we've eliminated partial updates. Instead, when a Fly Machine changes, we re-publish the entire data set for the Machine. Because of how Corrosion resolves changes to its own rows, the node receiving the re-published Fly Machine automatically filters out the no-op changes before gossiping them. Eliminating partial updates forecloses a bunch of bugs (and, we think, kills off a couple sneaky ones we've been chasing). We should have done it this way to begin with."
Community-side write-up: Self-healing Machine state synchronization and service discovery.
Trade-offs¶
- More bytes per publish — an update to one field sends the whole object. Acceptable when gossip is efficient and merge-layer no-op filtering prevents re-gossip.
- Requires the merge layer to do the work — this pattern is specific to CRDT / LWW systems. In a non-CRDT system, whole-object publish could itself introduce race conditions.
- Producer must hold the full object. If producers only see deltas (e.g. event-sourcing streams), you need to reconstruct the full object before publishing.
Generalisation¶
The pattern extends beyond Fly.io's Corrosion to any CRDT-backed state-distribution system where producers own the state they publish. Service discovery (whole-service records instead of individual endpoint deltas), config distribution (whole-config blobs instead of diffs), and fleet health (whole-node-state instead of per-metric deltas) all benefit.
Seen in¶
- sources/2025-10-22-flyio-corrosion — canonical primary source. "We should have done it this way to begin with."