Files
microdao-daarion/infrastructure/nats/worker-contract.md
Apple 8fe0b58978 🚀 NATS JetStream: K8s deployment + streams + job schema v1
- K8s deployment (2 replicas, PVC, initContainer для server_name)
- Streams definitions (MM_ONLINE, MM_OFFLINE, MM_WRITE, MM_EVENTS)
- Job payload schema (JSON v1 з idempotency)
- Worker contract (capabilities + ack/retry)
- Init streams script
- Оновлено ARCHITECTURE-150-NODES.md (Control-plane vs Data-plane)

TODO: Auth (nkeys), 3+ replicas для prod, worker-daemon implementation
2026-01-10 10:02:25 -08:00

4.1 KiB
Raw Blame History

Worker Contract v1 — Memory Module

Дата: 2026-01-10
Версія: 1.0.0


📋 Capability Registry

Кожен воркер реєструється з наступними capabilities:

{
  "node_id": "node-123",
  "tier": "A|B|C",
  "region": "eu-west",
  "trust_zone": "internal",
  "hardware": {
    "cpu_cores": 32,
    "ram_gb": 128,
    "gpu": true,
    "gpu_model": "RTX 3090",
    "vram_gb": 24,
    "cuda_version": "13.0"
  },
  "capabilities": {
    "max_batch": 1000,
    "max_tokens": 8192,
    "models": ["embed-multilingual-v3.0", "llama3:8b"],
    "embedding_dim": 1024,
    "supported_jobs": ["embed", "summarize", "index"]
  },
  "status": "ready|busy|maintenance",
  "last_heartbeat": "2026-01-10T19:30:00Z"
}

Реєстрація

  • Postgres (таблиця worker_capabilities) або Consul (KV store)
  • Heartbeat кожні 30 секунд
  • Якщо heartbeat пропущено > 90 секунд → worker вважається offline

🔄 Job Execution Flow

1. Pull Model

Воркер сам вирішує "чи брати job":

  • Підписка на consumer (durable)
  • Перевірка requirements vs власні capabilities
  • Якщо підходить → бере job, інакше пропускає

2. Execution

async def execute_job(job: JobPayload):
    # 1. Перевірка requirements
    if not can_fulfill(job.requirements, my_capabilities):
        await msg.nak()  # Negative ack, не підходить
        return
    
    # 2. Виконання
    try:
        result = await process_job(job)
        
        # 3. Запис результату
        await write_result(job, result)
        
        # 4. ACK
        await msg.ack()
        
    except RetryableError as e:
        await msg.nak(delay=e.backoff)
    except FatalError as e:
        await msg.ack()  # ACK щоб не retry
        await log_error(job, e)

3. ACK/NAK Policy

  • ACK — job виконано успішно
  • NAK — job не виконано, потрібен retry
  • NAK(delay) — retry з затримкою
  • Timeout — якщо ack_wait вийшов → автоматичний retry

📊 Consumer Policies

Online Consumer (Tier A)

stream: MM_ONLINE
consumer: online-worker-tier-a
ack_wait: 30s
max_ack_pending: 5000
concurrency: 50-200 (per worker)
filter: tier=A AND needs_gpu=true (для GPU jobs)

Worker Selection:

  • Тільки tier=A
  • needs_gpu=true → тільки GPU воркери
  • max_latency_ms перевірка

Offline Consumer (Tier B)

stream: MM_OFFLINE
consumer: offline-worker-tier-b
ack_wait: 5-30 min
max_ack_pending: 10000
concurrency: висока, але з backpressure

Backpressure:

  • Якщо online backlog росте → обмежити offline concurrency
  • Preemption: offline jobs можуть бути призупинені для online

🔍 Idempotency

Обов'язково: кожен job має idempotency_key (SHA256 hash).

Перевірка:

  • Перед виконанням перевірити в Postgres: чи вже виконано?
  • Якщо так → повернути кешований результат
  • Якщо ні → виконати та зберегти результат

TTL: idempotency keys зберігаються 7 днів.


📈 Metrics & Alerts

Worker Metrics

  • worker_jobs_processed_total{type, status}
  • worker_job_duration_seconds{type, quantile}
  • worker_gpu_utilization{node_id}
  • worker_vram_usage_bytes{node_id}
  • worker_errors_total{type, error_type}

NATS Metrics

  • nats_consumer_lag{stream, consumer}
  • nats_redeliveries_total{stream}
  • nats_ack_pending{stream, consumer}
  • nats_stream_storage_bytes{stream}

Alerts → Matrix Ops Room

  • MM_ONLINE backlog > 1000 (SLO порушення)
  • redeliveries spike > 100/min
  • embed p95 > 500ms (target: 300ms)
  • disk > 80% на JetStream PVC
  • worker offline > 2 min (Tier A)

Документ створено: 2026-01-10 19:30 CET