Files
microdao-daarion/docs/cursor/43_database_events_outbox_design.md
Apple c552199eed chore: organize documentation structure for monorepo
- 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
2025-11-15 04:08:35 -08:00

9.2 KiB
Raw Blame History

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: 100300 подій
  • 210 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 події можуть бути:

  • заархівовані після 90365 днів,
  • переведені у 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.md
  • 27_database_schema_migrations.md
  • 34_internal_services_architecture.md
  • 29_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