Real-time sync & conflict resolution

Concurrent edits can conflict. Jspreadsheet resolves conflicts with a central sequencer and last-write-wins. The server does not use operational transforms or CRDTs: with an authoritative server neither is required, and both add per-cell metadata that is costly on large documents.

Ordering: the server decides "when"

Every operation is applied to the document's single live instance on the server and stamped with a monotonic revision (rev). The revision defines the total order of events. Broadcasts to peers, acknowledgements to the sender, and REST responses all carry it, and socket connections deliver messages in server order.

The official client extension also serializes its own sends: operations wait in a FIFO queue and the next one is emitted only after the server acknowledges the previous one, so a client's operations reach the server in the order they were made.

Conflicts: the later write wins

Optimistic local apply

Edits apply to the local spreadsheet immediately, then travel to the server. The user never waits on the network to see their own change; a progress indicator stays visible while operations are unacknowledged.

Replay in server order

Each peer receives every other participant's operations as JSS broadcasts and replays them locally, in the order the server applied them. The state everyone converges toward is the server's: the operation the server sequenced last is the persisted value and what observers display.

The same-cell race

When two users write the same cell at nearly the same time, both see their own value first. The write that reaches the server last wins on the server and on every peer that replays the broadcasts. Replay does not compare incoming operations against unacknowledged local writes, so the client whose write was sequenced last can be left displaying the earlier value after replaying the in-flight broadcast; the cell settles again on the next operation that touches it, and the reconnection check below recovers from any lasting divergence.

Presence, not locks

Cell borders shared between collaborators are presence signals: they show which cells each user has selected without preventing anyone from typing. They bypass persistence, history and revision bumps: they are cosmetic, inexpensive, and removed when the user disconnects (the server broadcasts a resetBorders operation per worksheet). Locking cells against concurrent edits is not part of the model; locks coupled to network latency degrade editing responsiveness.

Missed messages and reconnection

Version signatures

Every data-changing operation carries a version signature, a UUID minted by the client that sent it. The server stores the latest signature on the live instance, and peers adopt it when they replay the operation, so all synchronized parties hold the same signature after the same operation.

Reconnection

After a disconnect, the client re-requests the document (load) and compares the returned version signature with its own. If they match, nothing was missed and the client resumes in place. If they differ, the client notifies the user and reloads the document.

Forced refresh

The server emits forceRefresh when local state can no longer be trusted: the persistence handler failed to save an operation, or the document was destroyed. The official client shows the message and reloads.

The revision counter

Independently of version signatures, the server stamps its monotonic rev on every acknowledgement, broadcast and load response. A custom client can track it to order operations and to detect gaps: a broadcast arriving with a revision higher than expected proves a missed operation, and reloading is the correct response. The official client extension currently relies on the version-signature check rather than revision tracking.

Why reload instead of replay

Reload-on-mismatch keeps the implementation simple: reconnects with missed changes are rare, and a reload is always correct. Undo history stays personal either way, because replayed remote operations never enter the local undo stack.