watershed Collaborative data structures for Gleam

← watershed · Runtime / Optimistic edits

Optimistic edits

Show a local edit the instant it happens, reconcile it when the server sequences it, and unwind it cleanly if it loses.

Traces apply · pending → ack_local → sequenced

Every edit is a bet on the order

When you set a value, watershed applies it to your local replica immediately and shows it, before the server has acknowledged anything. On the survey sheet that value is drawn in magenta: a revision not yet field-checked. It is a prediction of where the server's total order will place your edit, rendered at once instead of after a round-trip.

That is the whole appeal of optimism, and its whole risk: you are showing state that the sequence has not confirmed. watershed's job is to make the confirmation, or the correction, automatic.

Pending, then sequenced

Every local edit moves through one lifecycle. It is applied to your replica optimistically, queued outbound to the server, and, when the sequencer stamps it with a sequence number, acked back to you. That ack is the moment the edit stops being a prediction: ack_local promotes it from pending to sequenced, and on the sheet it re-prints from magenta to ink. Every other replica receives the same edit in the same position and applies it too.

import gleam/json
import watershed

// set applies to your replica *now* — get reads it back before the
// server has seen it. The UI never waits on the network.
watershed.set(survey, "depth-07", json.float(3.4))
let assert Ok(value) = watershed.get(survey, "depth-07")
// value == json.float(3.4), already visible. This value is *pending*: drawn in magenta,
// a prediction of where the server's order will place it.

Because the local apply is synchronous, get is read-your-writes: it returns your pending value the instant you set it. A map subscription reports visible local and remote changes: the optimistic write emits immediately, and a remote write emits when it is applied. An acknowledgement emits no event when it only confirms the already-visible value. Sequence numbers still determine the converged winner; subscriptions are a local view-change stream, not a sequenced-only feed.

When the bet loses

Optimism is only safe if losing is graceful. Two clients set the same key at once; only one can be last in the order. When the sequence disagrees with your prediction, the pending value simply resolves to whatever the order says: for a SharedMap that is last-write-wins, so a concurrent write with a later sequence number supersedes yours and the magenta re-prints as the winning ink value.

Some structures don’t apply a change optimistically at all. A claim is first-writer-wins: the request can show as pending, but shared claim state does not change until the server accepts it. If a peer already holds the slot, the refusal clears that pending request and a margin note records that you lost it. Watershed handles both optimistic correction and non-optimistic refusal; neither needs a rollback branch in your application.

Optimism never touches converged truth. Your pending value only ever changes what you see before the ack. The converged state is whatever falls out of the shared order, identical on every replica, so a mispredicted edit costs one silent re-print on your screen and nothing at all on anyone else's.

In short

Local edits render instantly as pending, promote to sequenced on ack, and resolve to the order's verdict when a concurrent write wins, automatically, with the same guarantee whether your prediction held or not.