139 lines
3.7 KiB
Markdown
139 lines
3.7 KiB
Markdown
# Синхронизация
|
|
|
|
## Цель
|
|
|
|
Один аккаунт Balance может использоваться на нескольких устройствах:
|
|
|
|
```text
|
|
iPhone
|
|
│
|
|
├── transaction A
|
|
├── category B
|
|
└── budget C
|
|
│
|
|
▼
|
|
BalanceServer
|
|
│
|
|
┌────┴────┐
|
|
▼ ▼
|
|
Android Mac
|
|
```
|
|
|
|
## Cursor
|
|
|
|
Каждый клиент хранит cursor последнего успешно применённого server change.
|
|
|
|
Принцип:
|
|
|
|
```text
|
|
cursor = 100
|
|
|
|
server:
|
|
101 A
|
|
102 B
|
|
103 C
|
|
|
|
pull(cursor=100)
|
|
→ A, B, C
|
|
→ new cursor = 103
|
|
```
|
|
|
|
Клиент должен сохранять cursor только после успешного применения соответствующего batch.
|
|
|
|
## Sequence
|
|
|
|
Server sequence задаёт глобальный порядок изменений в sync stream.
|
|
|
|
Требования:
|
|
- монотонность;
|
|
- отсутствие уменьшения;
|
|
- однозначность порядка;
|
|
- корректность при concurrent requests.
|
|
|
|
## Push
|
|
|
|
Push передаёт изменения клиента:
|
|
|
|
```text
|
|
local mutations
|
|
↓
|
|
POST /v1/sync
|
|
↓
|
|
validation
|
|
↓
|
|
ownership check
|
|
↓
|
|
persist
|
|
↓
|
|
append to change stream
|
|
```
|
|
|
|
Retry не должен создавать две разные логические записи из одного client change.
|
|
|
|
## Pull
|
|
|
|
Pull получает изменения после cursor.
|
|
|
|
Сервер не должен возвращать изменения другого пользователя.
|
|
|
|
При pagination клиент должен иметь возможность продолжить с нового cursor.
|
|
|
|
## Conflict resolution
|
|
|
|
Клиенты Balance используют timestamp-based versioning.
|
|
|
|
Типовая проверка:
|
|
|
|
```text
|
|
local.updatedAt < remote.updatedAt
|
|
```
|
|
|
|
Следовательно, server должен сохранять `updatedAt` достаточно точно и не подменять его произвольным временем каждого чтения.
|
|
|
|
При конкурентных writes сервер должен иметь однозначное правило победителя.
|
|
|
|
## Deletions
|
|
|
|
Удаление создаёт tombstone/change:
|
|
|
|
```json
|
|
{
|
|
"entity": "transaction",
|
|
"id": "...",
|
|
"deleted": true,
|
|
"updatedAt": "..."
|
|
}
|
|
```
|
|
|
|
Tombstone должен попасть в change stream так же, как обычное изменение.
|
|
|
|
## Idempotency
|
|
|
|
Push может повториться из-за:
|
|
- timeout;
|
|
- network failure;
|
|
- app restart;
|
|
- retry after 401/token refresh.
|
|
|
|
Поэтому сервер должен обрабатывать повторную отправку безопасно.
|
|
|
|
## Batch
|
|
|
|
Apple-клиент отправляет batches до 100 изменений и может делить крупные payloads.
|
|
|
|
Android должен использовать совместимый protocol.
|
|
|
|
Server не должен предполагать, что все изменения приходят одним запросом.
|
|
|
|
## Reset/full sync
|
|
|
|
Если client потерял cursor или требует reset, сервер должен поддерживать предусмотренный текущим API механизм полного pull либо новый cursor baseline.
|
|
|
|
Нельзя молча трактовать неизвестный/просроченный cursor как актуальный.
|
|
|
|
## Security
|
|
|
|
Sync endpoint всегда ограничен текущим authenticated user.
|
|
|
|
`deviceId` — идентификатор устройства, а не механизм авторизации.
|