Skip to content

feat(offline): deterministic conflict resolution and idempotent sync convergence - #1146

Open
zakariyaufarida5-wq wants to merge 2 commits into
rinafcode:mainfrom
zakariyaufarida5-wq:feat/1144-deterministic-offline-sync
Open

feat(offline): deterministic conflict resolution and idempotent sync convergence#1146
zakariyaufarida5-wq wants to merge 2 commits into
rinafcode:mainfrom
zakariyaufarida5-wq:feat/1144-deterministic-offline-sync

Conversation

@zakariyaufarida5-wq

Copy link
Copy Markdown

Summary

Makes offline sync deterministic and convergent for the IndexedDB queue (closes #1144):

  • Deterministic conflict resolution — per-record versioning with a version vector (version + updatedBy + Lamport logical clock) replaces pure timestamp LWW. Merges are stable regardless of device clock drift, with per-entity-type merge strategies (course_progress uses max-progress / OR-completion).
  • Idempotent replay — every queued operation carries a client-generated operationId; acknowledged ids are persisted in a dedupe map so duplicate delivery applies exactly once. Retries use capped exponential backoff, and operations that exhaust their retry cap move to a dead-letter queue (with manual retry).
  • Transactional batch drain — batches drain all-or-nothing inside a single IndexedDB transaction. A partial failure rolls back cleanly (no queue/ack/progress writes), and a persisted resumable cursor survives app restarts.
  • UI-facing conflict stateuseOfflineSync / useOfflineMode expose pending / conflicted / resolved counts plus conflicts; the ConflictResolver displays the state and both version vectors. Acked records are garbage-collected per the retention policy, and the service worker triggers a drain via background sync when connectivity returns.
  • Store reconciliation — after each drain the persisted store (synchronizationEngine / persistenceLayer) is updated with lastSynced and syncStatus and broadcast across tabs.

Tests

  • src/services/__tests__/offlineSync.test.ts — idempotent replay under duplicate delivery, operationId propagation, batch rollback on partial failure, cursor resume after restart, dead-lettering + retry, in-drain backoff retry, deterministic auto-merge across clock skew, manual conflict surfacing/resolution, retention GC, sync status.
  • src/lib/conflict/__tests__/resolver.test.ts — version-vector comparison, deterministic/commutative merges across reordered and clock-skewed inputs, per-entity strategies, legacy fallback.
  • Existing src/testing/conflict.test.ts and OfflineSyncManager.test.ts continue to pass (the latter was previously unloadable due to a broken relative import, now fixed).

CI

Type-check, lint, validate:ui, validate:web3, and next build all pass locally.

closes #1144

…convergence (closes rinafcode#1144)

- Version-vector based conflict detection and deterministic per-entity merge
  strategies, immune to device clock drift
- Idempotent replay via client-generated operation ids with ack dedupe,
  capped exponential backoff, and a dead-letter queue
- Transactional all-or-nothing batch drain with rollback and a resumable
  cursor that survives app restarts
- UI-facing pending/conflicted/resolved state via offline hooks and the
  ConflictResolver, plus retention GC for acked records and a service worker
  background-sync trigger
…concile sync engine

Keeps the deterministic offline-sync reconciliation while adopting the
realtime reconnect catch-up broadcast added in rinafcode#1143/rinafcode#1145.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Data Integrity] Deterministic offline conflict resolution and idempotent sync convergence across IndexedDB queues

1 participant