Перейти к содержанию

Case 07. Digital Wallet and Ledger

Практикум · Связанная теория · Редакционный стандарт

Тип материала — практический архитектурный кейс. HYPOTHETICAL — все численные параметры, сценарий и схемы. INFERENCE — выбор и критерии. UNKNOWN — фактическая производительность и параметры, которые предстоит согласовать.

1. Business problem

Предотвратить конкурентный перерасход и объяснить движение каждой денежной единицы по журналу.

2. Functional requirements

Резервировать, захватывать, освобождать и выполнять reversals. Поддержать idempotency и audit trail. Представлять booked, held и available.

3. Non-functional requirements

Spend decision имеет строгую consistency boundary. Read replicas служат только отображению с freshness marker. Сверка должна выявлять orphan holds и несбалансированные postings.

4. Assumptions и вопросы заказчику

Assumption — один writer на wallet, minor units и одна валюта на journal. Спросить overdraft policy, fees, cross-wallet transfer, currency scale, срок holds, спорные выплаты и legal reporting scope.

5. Оценка нагрузки и объёма данных

HYPOTHETICAL. Средняя скорость 1200 events/s, пик 2400 events/s, payload mean/p95/p99 650/1000/1700 B. Перцентили заданы отдельно для тестового профиля, среднее используется для объёма. Суточный профиль упрощён до постоянного среднего, накладные расходы протокола, индексов и WAL исключены.

Метрика Формула и подстановка Результат
Объём событий 1200 × 86 400 103680000 событий/сутки
Сырые данные 103680000 × 650 B 67.392 GB/сутки
Retention без сжатия 67.392 GB × 14 суток 943.488 GB
Broker copies при RF=3 943.488 GB × 3 2830.464 GB, из них 1886.976 GB дополнительные копии
Producer bandwidth, среднее 1200 × 650 × 8 / 10⁶ 6.24 Mbit/s
Producer bandwidth, пик 2400 × 650 × 8 / 10⁶ 12.48 Mbit/s
Partitions при 30% headroom max(ceil(2400 × 1,3 / 1 500), ceil(2400 × 650 × 1,3 / 1 800 000)) 3 partitions
Consumer parallelism ceil(2400 × 1,3 / 1 500) 3 workers на группу при принятой производительности
Backfill одних суток при 3× средней capacity и живом среднем потоке (1200 × 86 400) / (3 × 1200 − 1200) 43 200 с = 12 ч

GB и Mbit здесь десятичные. 1 500 events/s на partition/worker и 1,8 MB/s на partition — допущения теста. Для варианта без брокера строки RF/partitions относятся к альтернативному потоку или downstream outbox. RF не моделирует egress каждого consumer: добавьте по одной копии трафика на активную группу и репликационный трафик до RF−1 дополнительных копий. Пересчитайте отдельно при catch-up на пике, skew и p99 payload. Если capacity ≤ live rate, конечного времени восстановления нет.

Rate обозначает события, а не количество SQL statements. Если каждое событие порождает в среднем четыре записи, условный пик 2 400 × 4 = 9 600 writes/s до WAL и индексов. Провести отдельный профиль hot wallets.

6. Варианты архитектуры

A — transactional PostgreSQL ledger и balance guard в одной DB. B — event-sourced commands с одним writer на aggregate и replayable projection. Для перевода между агрегатами B требует отдельного settlement protocol.

7. Сравнение вариантов и выбор

Выбрать A для локальных многострочных инвариантов и прозрачной сверки. B подходит при чётком ownership и доказуемом single-writer. Синхронный guard нужен в обоих вариантах.

8. Component / data-flow

Иллюстративная схема выбранного варианта.

flowchart LR
 A["Wallet command"] --> B["Serialized balance guard"]
 B --> C["Ledger journal and holds"]
 C --> D["Outbox and audit"]

9. Normal processing sequence

sequenceDiagram
 participant A as Wallet command
 participant B as Serialized balance guard
 participant C as Ledger journal and holds
 participant D as Outbox and audit
 A->>B: Send identified input
 B->>C: Commit balanced legs with operation identity
 C-->>B: Return durable result and new position
 B->>D: Advance only with durable evidence

Проверить idempotency, lock wallet в детерминированном порядке, проверить available, записать hold или balanced legs, outcome и outbox. После commit обновить UI projection.

10. Failure / recovery sequence

sequenceDiagram
 participant B as Serialized balance guard
 participant C as Ledger journal and holds
 participant O as Recovery owner
 B->>C: Attempt write or advance
 Note over B,C: Concurrent debit or lost commit response
 O->>C: Read result by original key and reconcile
 C-->>O: Durable outcome or explicit gap

При двух запросах по 70 из 100 один удерживает 70. Второй перечитывает остаток 30. Потеря ACK разрешается по тому же ключу. Rollback отменяет незавершённую запись, reversal создаёт отдельную проводку по основанию.

11. Data model и contracts

Operation scope, canonical payload hash, journal ID, leg account/currency/amount, hold ID/state/expiry, wallet version и prior reference для corrections. Available = booked − active holds в выбранной модели.

12. Consistency boundaries

Hold и spend guard атомарны. Сумма legs по journal/currency равна нулю. Outbox commit совместный с journal. Внешний PSP outcome подтверждается отдельно.

13. Scaling и capacity planning

Шардировать по wallet owner только после measurement. Горячий wallet остаётся последовательным. Для multi-wallet locks использовать стабильный порядок, bounded retry для serialization/deadlock и durable terminal status.

14. Observability

Lock wait, conflict rate, available below zero, expired holds, outbox frontier и projection lag. Алерт на mismatch требует блокировки affected scope.

15. Data Quality и reconciliation

На одном cut проверить balanced groups, leg keysets, posted balances и активные резервы. Сравнить внешние payout reports с операциями. Общий zero sum пропускает сбалансированный дубль.

16. Security и compliance

Защитить операции изменения policy, разделить роли approval и posting. Audit связывает сотрудника, причину, journal и reversals. Read views с минимальными полями.

17. Implementation roadmap

Запустить single-wallet ledger, race/lost ACK tests, reconciliation dashboard, cross-wallet transfers и аварийный restore rehearsal.

18. Testing и acceptance criteria

Две независимые PostgreSQL сессии, rollback после записи первой leg, lost response, повтор reversal и stale projection. Acceptance — один outcome и durable journal, без перерасхода и orphan holds. SQLite fixture покрывает только локальную модель.

19. Trade-offs и ограничения

Single writer ограничивает hot-wallet throughput. Синхронная позиция снижает read latency, но становится contention point. Distributed transfer имеет отдельную границу.

20. Interview questions

Почему balance не достаточен для сверки? Одинаковый итог скрывает дубликат и пропуск. Что повторять после serialization failure? Всю транзакцию с тем же operation key и новой проверкой guard.

Первичные источники

Проверка источников: 2026-11-03. Документация описывает свойства компонентов. Результаты испытаний выбранной системы требуют отдельного evidence.