Изменено: 02.06.2026
Синхронизация построена вокруг очереди операций (SyncQueue). Модули приложения сначала меняют локальные данные, а затем sync постепенно отправляет накопленные изменения на сервер.
В реализации участвуют:
SyncManagerSyncRepositorySyncQueueSyncSchedulerSyncRemoteDataSourceSyncLocalDataSource
Главная точка входа - SyncManager.
requestSync()
forceSync(retry)
status: StateFlow<SyncStatus>
Во время работы используются состояния:
OkInProcessFailedWouldRetry(inSeconds)
Последнее состояние показывает, что запрос завершился ошибкой и система ждёт следующей попытки.
Запуск идёт через SyncScheduler.
Он наблюдает за очередью несинхронизированных операций:
queueRepo.getUnsyncedFlow()
После debounce создаётся запрос на синхронизацию.
В SyncManagerImpl используются:
MutexwithWebLock(...)SupervisorJob
Это нужно, чтобы несколько вкладок или несколько событий не запускали синхронизацию одновременно.
При ошибках используется экспоненциальная задержка:
10s
20s
40s
80s
...
до 5 минут
Во время ожидания статус переключается в WouldRetry(...).
Unsynced queue rows
-> mapSyncQueueRow(...)
-> SyncRequest
-> sendSyncRequest(...)
-> SyncResponse
На сервер отправляются накопленные операции из очереди.
Сервер возвращает несколько частей ответа.
acceptedIds используются для удаления успешно обработанных операций из локальной очереди.
deleteOperations применяются к локальному состоянию. В коде используются операции удаления счетов, категорий, переводов и транзакций.
updateState содержит актуальное состояние сущностей. Оно применяется через репозитории:
accountsRepo.upsertAccount(...)categoriesRepo.upsertCategory(...)transactionsRepo.badInsertTransfer(...)transactionsRepo.badInsertTransaction(...)
Без токенов синхронизация не запускается.
Перед отправкой запроса проверяется:
TokenStorage.isTokensEmpty()
Если пользователь не авторизован, запрос не выполняется.
Во время старта вызывается:
syncManager.forceSync(false)
За счёт этого локальное состояние пытается синхронизироваться с сервером сразу после инициализации приложения.