How sync works
Latest edit wins per record, deletions kept as markers, positions and epochs, and why a device never loses an edit it has not pushed.
Each person's records live in a small database of their own on the server, and on each of their devices. A record is a collection, an id, a time, and a JSON object, or a deletion marker. The server never looks inside the JSON.
The rules
- Per record, the latest edit wins, by the time the device made it. A device whose clock runs ahead is capped at the server's time, so a wrong clock cannot win forever. A tie keeps the change that arrived first.
- A change sent twice is harmless. It has the same time as the stored version, so it is ignored, and answered with that version.
- A losing edit learns what beat it. The push answer carries the version that won, and the device adopts it.
- Deletions are kept as markers, so they reach every device.
- Pulls go in order. Every stored change has a position; a device pulls what came after the last one it saw, and can stop and continue anywhere.
- A wiped store says so. Each pull names the store's epoch. If it changes, positions from before mean nothing and the device starts over.
- An edit not yet pushed is never overwritten. The device pushes it next time; if a newer version exists by then, it adopts that one.
Files
A record names its files by SHA-256, under $files in its data. The file is
uploaded first, once, and is private to the person who uploaded it. The server
cannot see which records point at which files, so the device deletes a file
once none of its own records uses it.
What a device does on each sync
- Compares the app's records with the versions last in step with the server.
- Uploads files that changed, then pushes the changed records, deletions first.
- Pulls what changed elsewhere and writes it in, skipping records changed on the device meanwhile.
Because a sync only compares states, a first sync, one cut short, and one after a long time offline all recover the same way.