Affected Package
locorda_core
Steps to Reproduce
- Installations A and B share a document at a common state: both at
logicalTime=3.
- A (offline) changes both
schema:name AND schema:description → A: logicalTime=4, physicalTime=T1.
- B (offline) changes only
schema:name → B: logicalTime=4, physicalTime=T2 where T2 > T1.
- A and B sync with each other.
Actual Behavior
LwwRegister.remoteMerge() computes ClockComparison.concurrent (each installation has logicalTime=4 for itself, neither has seen the other's entry). Tie-break falls back to maxPhysicalTime of the entire document: T2 > T1 → B wins all LWW properties. schema:description silently reverts to the pre-divergence value — B never changed it.
Expected Behavior
Each LWW property resolved independently:
schema:name: both sides changed it → physical-time tie-break → B wins (T2 > T1) ✓
schema:description: only A changed it → A wins unconditionally ✓
Platform
No response
Environment
No response
Additional Context
Root Cause:
The merge has no per-property change information available. Resolution granularity is the whole document. Two pieces of information are needed but currently missing or unused:
-
Per-property HLC in the document — when saving a LWW property, a metadata statement must be written recording the HLC at the time of that change (one statement per property, overwritten on each write → O(properties), bounded). This gives the remote side visibility into when each property was last changed.
-
Local DB query during concurrent merge — when ClockComparison.concurrent, for each LWW property:
- Local side: query
sync_property_changes table for whether/when this property was changed locally after the divergence point (the remote's last-known logical time for our installation)
- Remote side: read the per-property HLC statement from the remote document
- If only one side has a change after divergence → that side wins unconditionally
- If both sides changed it → physical-time tie-break (same as current behavior, now scoped to actually contested properties only)
Both pieces are needed. The per-property document statement ensures both sides reach the same merge decision (convergence). The local DB query gives our own side's change timestamps without requiring them to be re-embedded in the document on every sync.
RemoteDocumentMerger already has an injected _storage field (currently // ignore: unused_field with the comment // Storage will be needed for property change history during actual merge) and LwwRegister.remoteMerge() already contains:
// TODO: what about augmenting the CRDT merge with the property change metadata from DB for more precise merges?
Both confirm the fix was anticipated.
Affected Package
locorda_core
Steps to Reproduce
logicalTime=3.schema:nameANDschema:description→ A:logicalTime=4, physicalTime=T1.schema:name→ B:logicalTime=4, physicalTime=T2whereT2 > T1.Actual Behavior
LwwRegister.remoteMerge()computesClockComparison.concurrent(each installation haslogicalTime=4for itself, neither has seen the other's entry). Tie-break falls back tomaxPhysicalTimeof the entire document:T2 > T1→ B wins all LWW properties.schema:descriptionsilently reverts to the pre-divergence value — B never changed it.Expected Behavior
Each LWW property resolved independently:
schema:name: both sides changed it → physical-time tie-break → B wins (T2 > T1) ✓schema:description: only A changed it → A wins unconditionally ✓Platform
No response
Environment
No response
Additional Context
Root Cause:
The merge has no per-property change information available. Resolution granularity is the whole document. Two pieces of information are needed but currently missing or unused:
Per-property HLC in the document — when saving a LWW property, a metadata statement must be written recording the HLC at the time of that change (one statement per property, overwritten on each write → O(properties), bounded). This gives the remote side visibility into when each property was last changed.
Local DB query during concurrent merge — when
ClockComparison.concurrent, for each LWW property:sync_property_changestable for whether/when this property was changed locally after the divergence point (the remote's last-known logical time for our installation)Both pieces are needed. The per-property document statement ensures both sides reach the same merge decision (convergence). The local DB query gives our own side's change timestamps without requiring them to be re-embedded in the document on every sync.
RemoteDocumentMergeralready has an injected_storagefield (currently// ignore: unused_fieldwith the comment// Storage will be needed for property change history during actual merge) andLwwRegister.remoteMerge()already contains:// TODO: what about augmenting the CRDT merge with the property change metadata from DB for more precise merges?Both confirm the fix was anticipated.