- Create /docs structure (microdao, daarion, agents) - Organize 61 cursor technical docs - Add README files for each category - Copy key documents to public categories - Add GitHub setup instructions and scripts
9.2 KiB
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