Traktuj aplikację jako : każde urządzenie to lokalna . Akcje nauki są zapisywane do plus do dopisywanego tylko na końcu (outbox); po ponownym połączeniu urządzenie i od ostatniej synchronizacji. Konflikty rozwiązuje się jedną globalną regułą, lecz .
Traktuj aplikację jako : każde urządzenie to lokalna . Akcje nauki są zapisywane do plus do dopisywanego tylko na końcu (outbox); po ponownym połączeniu urządzenie i od ostatniej synchronizacji. Konflikty rozwiązuje się jedną globalną regułą, lecz .
Model mentalny: pomyśl o tym jak o gicie — każde urządzenie „commituje” lokalnie w trybie offline, a synchronizacja to merge, a nie nadpisanie.
Device (offline) Server = source of truth
┌──────────────────┐ on reconnect ┌───────────────────────────┐
│ Local DB (SQLite)│ push ops (delta) │ Sync API /sync │
│ + Op log (outbox)│ ─────────────────▶ │ • idempotent by op.id │
│ cursor = lastSeen│ ◀───────────────── │ • merge by semantics │
└──────────────────┘ pull changes>cursor└───────────────────────────┘
Progress DB (Postgres)
Zegary telefonów są niewiarygodne, a dwa urządzenia mogą edytować ten sam rekord offline, więc „last write wins” (LWW) po cichu gubi dane. Rozwiązuj według semantyki:
// op ma id -> stosuj idempotentnie (ponowienia nie liczą podwójnie). clientTs NIE jest ufany.
function mergeProgress(server, op) {
return {
// postęp tylko rośnie -> max: spóźniony stary op nigdy nie cofa go wstecz
progress: Math.max(server.progress ?? 0, op.progress),
// studiedMs to licznik addytywny -> dodaj deltę, nie nadpisuj
studiedMs: (server.studiedMs ?? 0) + op.studiedMs,
completedAt: server.completedAt ?? (op.progress >= 1 ? op.clientTs : null),
};
}
Tutaj zwykłe LWW pozwoliłoby, aby przeterminowane urządzenie synchronizujące się z opóźnieniem z progress: 0.3 nadpisało serwerowe 0.9 → utracony postęp. Wzięcie max i sumowanie studiedMs czyni synchronizację niezależną od kolejności — bezpieczną, gdy miliony urządzeń synchronizują się z rozbieżnymi zegarami.
Prowadzący sprawdza, czy wiesz, że zegarom klienta nie można ufać (więc nie czyste timestamp LWW), czy czynisz synchronizację idempotentną (op id), oraz czy mergujesz według znaczenia danych. Słaba odpowiedź: „użyj timestampów, last write wins”. Mocna odpowiedź: replica + op log + idempotencja + merge per-pole + serwer jako source of truth — i wyraźne nazwanie awarii utraty danych w LWW.
LWW jest tanie, ale stratne; semantyczny merge wymaga przemyślenia każdego pola, ale niczego nie gubi. Sięgaj po pełne CRDT/OT tylko wtedy, gdy potrzebujesz prawdziwej edycji współdzielonej (notatki, dokumenty) — jest kosztowne, więc nie używaj go do wszystkiego.
Biblioteka pytań rekrutacyjnych IT ze szczegółowymi odpowiedziami — od Juniora do Seniora.
Wesprzyj