Files
Balance/BalanceServer/docs/SYNC.md
T

3.7 KiB

Синхронизация

Цель

Один аккаунт Balance может использоваться на нескольких устройствах:

iPhone
   │
   ├── transaction A
   ├── category B
   └── budget C
        │
        ▼
   BalanceServer
        │
   ┌────┴────┐
   ▼         ▼
 Android    Mac

Cursor

Каждый клиент хранит cursor последнего успешно применённого server change.

Принцип:

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 передаёт изменения клиента:

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.

Типовая проверка:

local.updatedAt < remote.updatedAt

Следовательно, server должен сохранять updatedAt достаточно точно и не подменять его произвольным временем каждого чтения.

При конкурентных writes сервер должен иметь однозначное правило победителя.

Deletions

Удаление создаёт tombstone/change:

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