Skip to content

[claude] Sync: duplicated ClientId causes silent, permanent divergence — add detection and reconciliation #79

Description

@myieye

[Claude-drafted]

Sync exchanges commits purely on (ClientId → latest commit timestamp) (SyncState, QueryHelpers.GetMissingCommits). This assumes each ClientId is a single writer with append-only history. If two replicas ever write under the same ClientId (copied SQLite file, restored device backup), their histories fork and commits are permanently stranded in both directions — each side believes the other already has everything below its head. Nothing detects this: commit hashes never cross the wire ([JsonIgnore]), hash only id + parentHash, and are rewritten locally.

Repro: copy a project DB to a second client, edit + sync on both. The project eventually becomes unsyncable: an edit arrives for an entity whose creating commit is stranded, and SnapshotWorker throws on every subsequent sync.

Proposal (layered):

  1. Tripwire: in AddRangeFromSync, receiving a commit authored by the local ClientId that isn't already in the local DB proves the ID is duplicated → surface loudly; the app should switch to a fresh ClientId so the fork stops growing.
  2. Detect: extend SyncState entries with a commit count + order-independent digest of that client's commit IDs, compared over the shared range (≤ the lower head). Mismatch ⇒ divergence, even when heads differ. Must stay compatible with timestamp-only clients.
  3. Repair: on divergence for a ClientId, exchange that client's full commit-ID list, diff, send missing commits both ways. AddRangeFromSync already handles past-insertion (dedup, hash rewrite, snapshot replay), so the merge converges. Open question: auto-repair with loud logging vs. requiring user attention.

Prevention (keeping writer identity out of the copyable DB) is the app's job: sillsdev/languageforge-lexbox#2431.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions