Офлайн-режим и синхронизация
↑ 8.4 Frontend System Design · ← Предыдущая · → Следующая
Зачем это на собесе
Offline-first — сложный кейс; проверяют умение обеспечить консистентность.
Уровни поддержки
- Оболочка offline: Service Worker кэширует статику, приложение открывается.
- Чтение offline: данные в IndexedDB/кэше.
- Запись offline: очередь мутаций, отправка после восстановления сети.
- Полная синхронизация: несколько устройств, разрешение конфликтов.
Хранилища
| Хранилище | Особенности |
|---|---|
| localStorage | синхронный, ~5 МБ, строки |
| IndexedDB | асинхронное, большие объёмы, индексы (Dexie, idb) |
| Cache API | ответы HTTP (Service Worker) |
| OPFS | файловая система, большие файлы |
Очередь мутаций
type Mutation = { id: string; type: string; payload: unknown; createdAt: number; attempts: number }
async function enqueue(m: Mutation) { await db.outbox.put(m); tryFlush() }
async function tryFlush() {
if (!navigator.onLine) return
for (const m of await db.outbox.orderBy('createdAt').toArray()) {
try { await api.apply(m); await db.outbox.delete(m.id) }
catch (e) { if (isFatal(e)) await markFailed(m); break }
}
}
window.addEventListener('online', tryFlush)Требования: идемпотентность (ключ идемпотентности), сохранение порядка, ограничение повторов, показ статуса пользователю.
Конфликты
| Стратегия | Описание |
|---|---|
| Last write wins | побеждает последняя запись; просто, возможна потеря |
По версии (ETag, version) | сервер отвергает устаревшую, клиент сливает |
| Слияние по полям | конфликтуют только одинаковые поля |
| CRDT / OT | автоматическое слияние (коллаборативное редактирование) |
| Ручное разрешение | показать пользователю |
Синхронизация
- pull по
updatedSince/ cursor, push очереди; - фоновая синхронизация (Background Sync API, периодически);
- дельты вместо полной загрузки;
- миграции схемы IndexedDB;
navigator.onLineне гарантирует наличие сети: проверять реальным запросом.
UX
Индикатор состояния (offline, синхронизация, ошибка), понятные сообщения, безопасное поведение при истечении токена.
Вопросы с ответами
Как обеспечить, чтобы повторная отправка не создала дубль?
Идемпотентные операции: клиент генерирует ключ мутации, сервер запоминает и повторно возвращает результат.
Как разрешать конфликты?
По версии с отказом устаревших записей и слиянием, для текстов — CRDT, в критичных случаях — вручную.