🚀 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
This commit is contained in:
Apple
2026-01-10 10:02:25 -08:00
parent 3478dfce5f
commit 8fe0b58978
7 changed files with 1046 additions and 35 deletions

View File

@@ -0,0 +1,164 @@
# Worker Contract v1 — Memory Module
**Дата:** 2026-01-10
**Версія:** 1.0.0
---
## 📋 Capability Registry
Кожен воркер реєструється з наступними capabilities:
```json
{
"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
```python
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)
```yaml
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)
```yaml
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*