Build1 publisher3 min readPublished
Wall-clock last-write-wins overwrote an offline phone note while the phone's clock read correctly
Wall-clock last-write-wins in a developer's notes app overwrote a phone edit made nine hours before it synced, though the phone's clock was right. The log shows two concurrent edits, a case the replacement hybrid logical clock, as described, still settles by keeping one.
The Engineer · Build desk

What happened
- A developer saved a note on an offline phone, then rewrote its first line on a laptop forty minutes later, before the phone reconnected.
- Each device stored notes in SQLite with updated_at set to Date.now() at write time, and the server kept whichever row carried the larger value.
- The wall-clock rule dated from eighteen months earlier and shipped with a code comment saying to revisit it if multi-device note editing became common.
- The replacement is a hybrid logical clock with two integer fields per row, advanced on local writes, reconciled on sync and compared lexicographically.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- contradiction The post blames clock ordering while its own log puts the laptop write second, so the fault to fix is how the app handles two concurrent edits to one body.
- constraint A hybrid logical clock settles two edits made with no sync between them by ranking their stamps, so one body is still discarded unless the design adds a merge.
- cost Keeping both edits means taking on the merge work the developer set aside, with five note attributes merging independently and a test burden a textarea feature could not justify.
- decision A 'revisit if' comment needs a counter behind it; without a measure of multi-device writes per note, users become the alert that the condition has arrived.
The repro log in the post contradicts the post's diagnosis. The laptop's timestamp is 300 higher than the phone's [14]. By the log's own t column, the phone wrote at t=0 and the laptop at t=300. The laptop reached the server at t=310, and the phone stayed offline until t=9h20m [11]. The server kept the later write [11]. Neither device had received the other's edit when it made its own [15].
The developer explains it this way. "The phone edit was newer in human time. It was older in wall-clock time, because the phone had been offline when it was made and the clock on the edit was set at write time, not at sync time," they wrote [12]. The log above that sentence has the phone writing first. So does the Tuesday account, where the laptop rewrite came forty minutes after the phone note [2]. The same post says "The phone's clock was fine. The server's clock was fine." [10] A correct clock read at write time is what updated_at already held, because it was Date.now() at write time [4].
What the log records is two edits forked from one base, and a table with one body column [4][15]. The server overwrote the loser, as designed [4]. With a correct clock the loser is the earlier edit. Here the earlier edit was the note the user had carefully written that morning [19].
The replacement is careful work. Each row gets hlc_ts and hlc_count, both monotonically increasing [13]. A local write increments the count. A received write moves ts to the larger of the local and remote values and bumps the count on a tie, and stamps compare lexicographically on (ts, count) [13]. A device that has pulled a write moves its ts up to that write's before stamping anything new, whatever its own wall clock says [13].
The Tuesday case never reaches the receive rule. No sync ran between the two edits, so the outcome is a lexicographic comparison of two stamps, and a comparison produces one winner [16]. The text ends at the comparison rule, so it does not show whether the new design keeps the losing body. In my view, keeping both edits takes a text merge or a conflict copy of the body, and a better clock only changes which edit is dropped.
The original decision holds up. When the note feature was a textarea and a save button, the alternative was a vector clock or CRDT with body, title, tags, reading-plan anchor and deleted flag merging independently [7]. "Offline sync is one of those areas where you can spend six weeks on an edge case that three users will ever touch," the developer wrote [8]. The code comment named its own trigger: revisit if multi-device note editing becomes common [5]. "The problem is nobody revisits," they wrote [9].
That trigger was countable. It was the share of accounts with two or more devices writing the same note in the same week, which the developer says went from never measured to a support-inbox topic every few days once reading plans shipped [6]. Nobody counted it, so the support inbox did. I'd expect a per-note count of writing devices, logged from the first sync, to have flagged the change before users did. Finding out from users cost eleven seconds of user-visible weirdness and about four hours of the developer's week [3].
What to watch
- Whether the rest of the post shows the hybrid-clock version keeping or surfacing the losing body when two edits share no sync between them.
- Support reports of clobbered notes after the hybrid logical clock ships; if they continue, the losses came from concurrency and the clock change did not address them.
- Whether the developer moves the note body to a vector-clock or CRDT merge, the option set aside eighteen months ago.