How Cloudflare Computer Handles Last-Write-Wins Conflicts in Multi-Container Scenarios
Cloudflare Computer resolves concurrent write conflicts using last-write-wins semantics at the sync level, where the last container push to reach the Durable Object silently overwrites all previous changes without merging or raising errors.
When multiple containers in the cloudflare/computer repository concurrently modify shared state backed by Durable Objects, the runtime must reconcile divergent histories. Understanding how last-write-wins conflict semantics operate in multi-container scenarios is essential for building reliable distributed workloads that avoid silent data loss.
The Last-Write-Wins Architecture
Sync-Level Granularity
Conflict resolution in Computer operates at the granularity of an entire sync batch rather than individual files. When a container calls Workspace.push(), it transmits a batch of changes to the Durable Object (DO). If multiple containers push concurrently, the DO applies each batch in the order received. The batch that arrives last becomes the definitive state, overwriting any earlier writes to the same paths. As documented in the sync protocol specification, this behavior mirrors a shared filesystem without locking: whichever write reaches storage last persists, and no merge logic is executed docs/02_sync_protocol.md#L341-L353.
Local Tree Replacement During Apply
When the DO receives an upstream entry that conflicts with local state, the runtime performs a complete replacement of the local node tree. The source code explicitly notes that this implements "last-writer-wins conflict handling" by removing the local node and its entire subtree, then applying the upstream entry in its place packages/computer/src/workspace.ts#L107-L110. This means any local children residing under a conflicting path are discarded without generating tombstones or conflict markers.
Safety Characteristics and Limitations
Safe Multi-Container Patterns
Last-write-wins semantics are safe and efficient when containers operate on disjoint paths, when there is a single writer per path, or when files are write-once (such as generated build artifacts or scaffolded configuration files). In these scenarios, the arrival order of pushes does not affect correctness because no two containers modify the same data simultaneously docs/02_sync_protocol.md#L357-L366.
Unsafe Patterns and Lost Updates
Concurrent read-modify-write cycles on shared state produce lost updates under LWW semantics. For example, if two containers independently read a counter value of 5, increment it to 6, and push concurrently, the DO will end with 6 rather than 7 because the second push simply overwrites the first. The protocol provides no indication that an update was lost, making LWW unsuitable for collaborative editing, shared counters, or append-only logs without external coordination docs/02_sync_protocol.md#L374-L386.
Practical Code Examples
The following examples demonstrate how concurrent pushes resolve in practice using the Workspace API.
Concurrent Overwrite Scenario
When two containers write to the same file, the second push prevails:
import { Workspace } from "@cloudflare/computer";
// Container A executes first
const wsA = new Workspace({ storage: ctxA.storage });
await wsA.fs.writeFile("/shared/state.txt", "version-from-A");
await wsA.push(); // Arrives at DO first
// Container B executes concurrently
const wsB = new Workspace({ storage: ctxB.storage });
await wsB.fs.writeFile("/shared/state.txt", "version-from-B");
await wsB.push(); // Arrives second and wins
// Subsequent pull on any container sees only version-from-B
const ws = new Workspace({ storage: doStorage });
await ws.pull();
const content = await ws.fs.readFile("/shared/state.txt");
console.log(content); // => "version-from-B"
Safe Disjoint Paths
Writes to different paths do not interfere, making arrival order irrelevant:
const ws = new Workspace({ storage: ctx.storage });
// These writes are independent and safe under LWW
await ws.fs.writeFile("/workspace-a/config.json", "{}");
await ws.fs.writeFile("/workspace-b/config.json", "[]");
await ws.push(); // Atomic batch containing both writes
Detecting Lost Updates (Manual Verification)
Because the runtime does not throw errors on conflict, applications must verify state if consistency is critical:
// Read current value
const before = await ws.fs.readFile("/counter.txt", "utf-8"); // "5"
// Modify locally
const next = (parseInt(before) + 1).toString();
await ws.fs.writeFile("/counter.txt", next); // Write "6"
// Push may succeed but be overwritten by another container's "6"
await ws.push();
// Verification requires re-reading after next pull
await ws.pull();
const after = await ws.fs.readFile("/counter.txt", "utf-8");
if (after !== next) {
console.warn("Update lost due to concurrent write");
}
Key Implementation Files
docs/02_sync_protocol.md– Defines the last-write-wins protocol semantics and documents safe versus unsafe usage patterns.packages/computer/src/workspace.ts– Implements theWorkspacefaçade and contains the core comment describing LWW tree replacement logic.
Summary
- Last-write-wins operates at the sync batch level, not individual file granularity.
- The last push to arrive at the Durable Object wins; earlier pushes are silently discarded without merge attempts or error generation.
- Local children under conflicting paths are removed without tombstones when upstream entries arrive.
- LWW is safe for disjoint paths and write-once files, but dangerous for read-modify-write cycles on shared state where updates may be lost.
Frequently Asked Questions
Does Computer detect conflicts and raise errors?
No. The Cloudflare Computer runtime implements silent last-write-wins resolution. When conflicting pushes arrive, the system applies the latest batch and discards earlier ones without throwing exceptions, returning conflict markers, or logging warnings. Applications must implement their own concurrency controls if detection is required.
What happens to local changes when an upstream conflict arrives?
When a Workspace.pull() receives an upstream entry that conflicts with local state, the runtime removes the local node and its entire subtree, then applies the upstream entry. Any local-only children under the conflicting path are permanently discarded without generating tombstones, as explicitly noted in the source packages/computer/src/workspace.ts#L107-L110.
Can I implement optimistic concurrency control or custom merge logic?
The built-in sync protocol does not support conditional puts or application-defined merge functions. To implement optimistic concurrency, you must layer your own versioning metadata (such as vector clocks or timestamps) on top of file contents and handle reconciliation in application code after calling Workspace.pull().
How does last-write-wins compare to CRDTs?
Unlike Conflict-free Replicated Data Types (CRDTs), which guarantee convergence through mathematical properties that preserve all concurrent updates, Computer's LWW semantics prioritize simplicity and performance by discarding all but the last write. CRDTs are suitable for collaborative editing where no update should be lost, whereas LWW is optimized for containerized batch jobs where writers typically own disjoint dataset partitions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →