watershed Collaborative data structures for Gleam

← watershed · SharedMap · a common miswiring

A counter is not
a map cell.

SharedMap resolves each key to the write with the higher server sequence number. That gives every replica the same value, but it does not preserve read-modify-write intent. If two clients read the same count and each writes count + 1, both submit the same replacement value, and the later write adds nothing. Here it is, live, through watershed's compiled map_kernel.

Two gauge houses at one lock keep the day's boats-locked tally in one shared map cell. Run the race and watch a boat vanish, then run the fix.

SharedMap · one shared key map_kernel · boats-locked

Upstream house

Client A
boats-locked 41

Downstream house

Client B
boats-locked 41

    Fix one · make the writes commute

    Nothing is wrong with the map: a counter just is not a single mutable cell. Give each replica its own key and sum them, and the two writes never contend. The same race now converges on the right total, through the same map_kernel. This layout (one positive-only column per replica) is the shape of a G-counter. A true G-counter also merges those columns by pairwise maximum, so duplicate delivery is idempotent.

    Per-replica keys · sum map_kernel · boats-locked/a + boats-locked/b

    Upstream house

    Client A
    boats-locked · sum 41 boats-locked/a 20

    Downstream house

    Client B
    boats-locked · sum 41 boats-locked/b 21

      Fix two · ship the delta

      Watershed's SharedCounter goes further: the op is the delta. A client never reads the tally to write it back. It sends increment(+1), or increment(−1) for a correction, and signed deltas sum the same in any order. This rig runs the compiled counter_kernel, the SharedCounter engine itself, not a map at all.

      SharedCounter · signed delta ops counter_kernel · boats-locked

      Upstream house

      Client A
      boats-locked 41

      Downstream house

      Client B
      boats-locked 41