43 — Database Events Outbox Design (MicroDAO)¶
Outbox Pattern: транзакційна доставка подій, таблиця outbox_events, воркери, дедуплікація, retry, backpressure, інтеграція з NATS JetStream
1. Purpose & Scope¶
Outbox Pattern вирішує проблему:
Як гарантувати, що подія завжди буде доставлена у NATS без втрати даних, навіть якщо сервіс або NATS відмовили?
Цей документ визначає:
- структуру
outbox_events, - процес вставки,
- воркерів,
- правила retry/backoff,
- дедуплікацію,
- atomic commit,
- safety механізми,
- інтеграцію з JetStream.
2. Why Outbox Pattern Is Required¶
У DAARION.city outbox використовується для:
- agent-run events,
- payouts,
- staking,
- wallet tx,
- embassy updates,
- oracle readings,
- governance updates,
- usage increments.
Причини:
- JetStream може бути недоступним у момент транзакції.
- Сервіси не повинні втрачати події.
- Потрібно гарантувати idempotency.
- Потрібно відокремити business logic від доставки.
3. Outbox Table Schema¶
create table outbox_events (
id text primary key,
stream text not null, -- e.g. STREAM_RWA
topic text not null, -- e.g. rwa.inventory.updated
payload jsonb not null,
created_at timestamptz default now(),
processed bool default false,
processed_at timestamptz,
error text -- optional failure reason
);
Вимоги:¶
idгенерується UUID або snowflake.- Рядки ніколи не видаляються (тільки retention archive).
processed=falseозначає «готовий до доставки».
4. Outbox Event Insertion¶
Подія вставляється у тій самій транзакції, що і бізнес-операція.
Приклад (псевдо):
BEGIN;
UPDATE payouts SET status='generated' WHERE id='p_001';
INSERT INTO outbox_events (
id, stream, topic, payload
) VALUES (
gen_id(),
'STREAM_PAYOUT',
'payout.generated',
'{"payout_id":"p_001","team_id":"t_555"}'::jsonb
);
COMMIT;
Гарантія:
- якщо COMMIT відбувся → подія потрапила у Outbox.
- якщо COMMIT не відбувся → подія не існує.
5. Outbox Worker Architecture¶
Outbox Worker →
SELECT * FROM outbox_events
WHERE processed=false
ORDER BY created_at
LIMIT 200;
for each event:
TRY publish to NATS
ON success → mark processed
ON failure → retry later
Workers можуть бути:
- 1..N штук,
- статично або auto-scaled,
- у Mesh під mTLS.
6. Worker Processing Loop¶
while true:
events = db.fetch_unprocessed(limit=200)
for evt in events:
try:
nats.publish(evt.topic, evt.payload)
db.mark_processed(evt)
except NATSDownError:
sleep(backoff)
continue
except ValidationError:
db.mark_error(evt, "invalid payload")
continue
7. Deduplication¶
Необхідно витримати at-least-once, але уникати дублювань:
- JetStream має natural dedupe по
msg_id - Outbox Worker використовує:
NATS header: Nats-Msg-Id = outbox_event_id
Приклад:
nats.publish(
topic,
payload,
headers={"Nats-Msg-Id": evt.id}
)
JetStream не доставить дубль.
8. Retry Strategy¶
8.1 Backoff¶
Експоненційний:
1s → 2s → 4s → 8s → 16s → max 60s
8.2 Dead-letter Condition¶
Після X помилок:
processed=false
error="unrecoverable"
worker перестає ретрити; адміністратор розбирається вручну.
9. Batch Processing & Throughput¶
Рекомендації:¶
- batch size: 100–300 подій
- 2–10 worker replicas
- автоматичний autoscaling по lag:
- якщо кількість
processed=false> 10,000 → scale up
10. Event Ordering Rules¶
Outbox Worker дотримується ordering:
- per stream
- per partition key (custom)
Порядок подій важливий для:
- payouts
- RWA updates
- governance
11. Multi-Stream Routing¶
Сервіс визначає stream:
if topic startswith "agent.run":
stream = STREAM_AGENT_RUN
if topic startswith "embassy":
stream = STREAM_EMBASSY
Усі стріми повинні бути попередньо створені:
{
"name": "STREAM_RWA",
"subjects": ["rwa.inventory.*"],
"replicas": 3,
"storage": "file"
}
12. Failure Modes¶
12.1 Worker Crash¶
Рішення:
- worker restart → обробляє далі.
12.2 Database Down¶
Outbox Worker призупиняється → не доставляє події.
12.3 NATS Down¶
Worker робить retry до відновлення.
12.4 Corrupted Payload¶
Worker позначає error і пропускає подію.
13. Safety Guarantees¶
Outbox забезпечує:
- atomicity
- consistency
- at-least-once
- no-loss
- event replayability
- jetstream dedupe
- idempotent consumers
14. Event Consumer Rules¶
Кожен сервіс, що слухає події, дотримується:
- idempotency (повтор повинен не ламати логіку),
- durable consumer,
- manual ack,
- retry on failure,
- trace_id propagation.
15. Operational Metrics¶
Моніторимо:
- outbox_events total count
- unprocessed count
- processing rate
- worker lag
- NATS publish latency
- error rate
16. Backpressure Control¶
Якщо worker не встигає:
- автоматичний autoscaling,
- або backpressure для producer services (м'яке гальмування).
17. Batch Deletion / Archival¶
Outbox події можуть бути:
- заархівовані після 90–365 днів,
- переведені у cold storage (S3),
- видалені після 3 років (policy-dependent).
18. Example End-to-End Flow (Payout)¶
sequenceDiagram
participant W as Wallet Service
participant DB
participant OUT as Outbox Worker
participant NATS
W->>DB: tx begin
W->>DB: insert payout
W->>DB: insert outbox_event(payout.generated)
DB-->>W: commit
OUT->>DB: fetch unprocessed
OUT->>NATS: publish payout.generated (msg_id=evt_id)
NATS-->>OUT: ack
OUT->>DB: mark processed
19. Integration with Other Docs¶
Цей документ доповнює:
42_nats_event_streams_and_event_catalog.md27_database_schema_migrations.md34_internal_services_architecture.md29_scaling_and_high_availability.md
20. Завдання для Cursor¶
You are a senior backend engineer. Implement Database Events Outbox Design using:
- 43_database_events_outbox_design.md
- 42_nats_event_streams_and_event_catalog.md
- 27_database_schema_migrations.md
Tasks:
1) Create outbox_events table schema (id, stream, topic, payload, created_at, processed, processed_at, error).
2) Implement Outbox Event Insertion (atomic transaction with business logic).
3) Create Outbox Worker service (fetch unprocessed, publish to NATS, mark processed).
4) Implement Worker Processing Loop (batch processing, error handling).
5) Add Deduplication (NATS header Nats-Msg-Id = outbox_event_id).
6) Implement Retry Strategy (exponential backoff, dead-letter condition).
7) Configure Batch Processing & Throughput (batch size 100-300, autoscaling).
8) Add Event Ordering Rules (per stream, per partition key).
9) Implement Multi-Stream Routing (topic → stream mapping).
10) Handle Failure Modes (worker crash, database down, NATS down, corrupted payload).
11) Add Safety Guarantees (atomicity, consistency, at-least-once, no-loss, replayability).
12) Define Event Consumer Rules (idempotency, durable consumer, manual ack, retry, trace_id).
13) Add Operational Metrics (outbox_events count, unprocessed count, processing rate, worker lag, NATS latency, error rate).
14) Implement Backpressure Control (autoscaling, producer backpressure).
15) Add Batch Deletion / Archival (90-365 days archive, S3 cold storage, 3 years deletion).
Output:
- list of modified files
- diff
- summary
21. Summary¶
Outbox Design гарантує:
- надійну доставку подій у JetStream,
- 100% уникнення втрати даних,
- консистентність життєво важливих RWA/payout/embassy процесів,
- контрольовану доставку у high-load,
- retry/backoff без дублювань,
- зручну діагностику й моніторинг.
Це — основний транспортний транзакційний шар DAARION OS.
Версія: 1.0
Останнє оновлення: 2024-11-14