Sync internals

The sync overview explains the model; this page states the mechanics precisely, for implementers of custom clients and for anyone auditing the design.

Guarantees

G1: Total order per document

All operations on a document are applied by one server process, on one event loop, to one live instance. The revision rev is incremented in the same synchronous block as the engine apply, so revision order equals apply order, and there is no window in which two operations hold the same position.

G2: Per-connection FIFO

Socket.IO delivers messages on one connection in the order the server emitted them. Since the server emits each operation's broadcast (to peers) and acknowledgement (to the sender) at apply time, a client observes events in server order on its own connection.

G3: Serialized client sends

The official client extension keeps at most one operation in flight: outgoing operations wait in a FIFO queue, and the next emit happens only inside the acknowledgement callback of the previous one. A client's own operations therefore reach the server, and are sequenced, in the order the user made them.

Revision bookkeeping

Where the revision is bumped

The server increments rev immediately after applying an operation to the live instance, inside the same synchronous block, starting from 0 when the document enters the cache. Presence operations (setBorder/resetBorders) skip persistence and do not bump the revision.

What carries the revision

  • The JSS acknowledgement to the sender: { status: true, rev }.
  • The JSS broadcast to peers: second argument after the zipped payload.
  • The load acknowledgement: second argument after the zipped configuration.
  • REST write responses: { "message": "Done", "rev": <n> }. REST writes run through the same instance and counter, and are broadcast to the socket room with the same revision.

Gap rule for custom clients

Because acknowledgements and broadcasts arrive in order on one connection (G2), a client can track rev as the maximum seen. A broadcast carrying rev > local + 1 proves a missed operation; local state can no longer be trusted, and reloading the document is the correct response. Presence broadcasts carry the current revision without bumping it, so repeated revisions are normal. The official client extension does not track rev; it relies on version signatures (below).

Version signatures

How signatures propagate

For every non-border operation, the sending client mints a UUID, stores it as the spreadsheet's version, and includes it in the payload. The server writes it to the live instance (config.version), and every peer adopts it when replaying the broadcast. After any given operation, all synchronized parties hold the same signature.

Reconnection check

When the socket reconnects after a drop, the client emits load and compares the returned configuration's version with its own. Equal signatures mean nothing was missed and the client resumes in place; different signatures trigger a user notification and a page reload.

REST writes and signatures

Operations applied through the REST API carry no version field, so applying one overwrites the stored signature. Socket clients holding an older signature will reload on their next reconnection check; connected clients receive the operation itself over the broadcast.

Remote replay on the client

Suppressing echo and history

While replaying a broadcast, the client sets an ignoreCloud flag so the replayed mutation is not re-sent to the server, and suppresses the local undo history so remote edits never enter the local undo stack. Operations whose user is assistant are the exception: AI-originated changes are recorded in local history, so the user can undo them.

Border operations

setBorder and resetBorders are replayed with the originating user id appended, which keys each collaborator's selection border. On disconnect, the server broadcasts resetBorders per worksheet to clear that user's borders everywhere.

Scope and known limits

Same-cell concurrent writes

Replay applies remote operations in server order without comparing them to unacknowledged local writes. Observers converge on the server's state; the client whose write was sequenced last may transiently display the earlier in-flight value until the next operation on that cell, and lasting divergence is corrected by the reconnection check.

Structural operations and index shift

Concurrent structural operations (a row insert racing a cell write addressed by index) converge, because every peer replays the same sequence and computes the same final state, but index-addressed writes land on the position after the shift, which may not be the row the writer was looking at.

Undo is strictly personal

Replayed remote operations never enter the local undo stack (except assistant operations, by design), so undo reverts only your own edits. The server engine records no history at all.

Verifying an implementation

Test convergence rather than visual output: two real clients on one document, N rounds of same-cell writes with randomized firing order and jitter; after each round, assert client A, client B and a direct server read agree on the final value, and that both clients hold the server's version signature. Timing-dependent interleavings surface intermittently, so run enough rounds to exercise them.