Requirements:
- Offline-first for 5M users, batched uploads when online, conflict resolution, energy-aware scheduling, reasonable latency and scalability.
High-level architecture:
- Clients maintain local DB + operation log (oplog). Server exposes sync APIs and an authoritative per-entity state store with versioning (vector clocks / causal metadata). A sync gateway (stateless) handles auth, rate limiting, and routes to sync workers that apply ops and run conflict resolution. Persistent queue (Kafka) for downstream processing and analytics.
Client data model (oplog vs state merge):
- Use oplog as primary local model: record user intents (create/update/delete) as immutable operations with timestamps and client ID, causal metadata and a per-client monotonically increasing sequence. Apply ops locally to a derived state for immediate UI responsiveness (optimistic). Keep tombstones for deletes.
- Periodic compaction: convert oplog into checkpointed state to bound storage.
Server APIs:
- POST /sync/push {client_id, last_sync_seq, ops[]}
- GET /sync/pull?since=server_seq&client_id=...
- POST /sync/ack {client_id, ack_seq}
- GET /entity/{id}?v=...
APIs are idempotent, accept batched ops and return per-op result + conflict hints.
Conflict resolution strategy:
- Prefer CRDTs where data type permits (counters, sets, registers) for automatic, safe merges.
- For complex documents: hybrid approach — server tries deterministic merge using operational transforms or per-field CRDTs; fallback to last-writer-wins using vector clocks for simple fields where LWW is acceptable.
- Expose manual merge for user-visible conflicts: create a conflict record, notify client UI with both versions and metadata; allow user to resolve which produces a final op applied to server and propagated to other clients.
Energy-aware scheduling & batching:
- Defer syncs to energy-friendly windows: when device is charging, on Wi‑Fi, or meets a backoff schedule. Use OS job schedulers (WorkManager on Android, BGTasks on iOS) to batch network use.
- Adaptive backoff: short aggressive retries while app foreground; otherwise exponential backoff. Bundle operations into payloads up to reasonable size and compress diffs (protobuf/gzip).
- Network-awareness: avoid background sync on cellular unless user opted-in; use low-power radios scheduling.
- Prioritize critical ops (payments) for immediate sync; low-priority ops wait for batch windows.
Scalability & robustness:
- Shard server state by user ID; use causal vectors truncated via dotted-versioning to limit metadata size. Use background reconciliation jobs to garbage collect tombstones after safe windows.
- Monitor conflict rates; if high, consider schema or UX changes to reduce simultaneous edits.
Trade-offs:
- Oplog + CRDTs increases client storage and metadata complexity but gives correct merges and offline UX. LWW is simpler but can lose updates.
- Energy-aware delays add eventual consistency latency for non-critical data.
This design balances correctness (CRDTs/explicit merges), scalability (batched APIs, sharding), and low energy impact (OS schedulers, batching, network-awareness).