Files
Balance/BalanceServer/docs/SYNC.md
T

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` — идентификатор устройства, а не механизм авторизации.