Runbook: Kuyruk birikmesi (queue backlog) ve DLQ
Alarmlar: QueueDepthHigh (waiting + prioritized > 50k, 10 dk; öncelikli işler BullMQ'da prioritized kümesindedir), DeadLetterQueueHigh (failed > 100), WorkerJobsStalled (iş var, tamamlanan yok), WorkerDown. Pano:…
Alarmlar: QueueDepthHigh (waiting + prioritized > 50k, 10 dk; öncelikli işler BullMQ'da prioritized kümesindedir), DeadLetterQueueHigh (failed > 100), WorkerJobsStalled (iş var, tamamlanan yok),
WorkerDown. Pano: Grafana Kuyruklar. Kuyruklar ve öncelikler: apps/worker/src/queues.ts.
1. Hangi kuyruk?
# compose
docker compose --env-file .env.prod -f infra/compose/docker-compose.prod.yml exec redis \
redis-cli -a "$REDIS_PASSWORD" --no-auth-warning eval "local r={} for _,q in ipairs(ARGV) do r[#r+1]=q..'='..redis.call('llen','hs:'..q..':wait')..'+'..redis.call('zcard','hs:'..q..':prioritized')..'/'..redis.call('zcard','hs:'..q..':delayed')..'/'..redis.call('zcard','hs:'..q..':failed') end return r" 0 \
scheduler audience dispatch-ios dispatch-android dispatch-hms dispatch-web dispatch-email dispatch-sms events webhooks journeys maintenance
# platform admin: GET /admin/v1/system/health (kuyruk derinlikleri + worker heartbeat'leri)
```text
| Kuyruk | Normal neden | Tipik çözüm |
|--------|--------------|-------------|
| `audience` | 1M+ abonelik kampanyası, eşzamanlılık 2 | bekle; `worker-dispatch` replikası artır |
| `dispatch-*` | büyük kampanya (200 teslimat/batch), sağlayıcı yavaş/rate limit (`delayed` artar) | `provider-outage.md`; `WORKER_CONCURRENCY` ve replika artır |
| `events` | SDK/API olay patlaması (10k olay/sn hedefi) | `worker-system` replikası artır; `events` kuyruğunu ayrı servise al |
| `webhooks` | müşteri endpoint'i yavaş (10 sn timeout, 1 eşzamanlı) | endpoint sağlığı; `WEBHOOK_TIMEOUT_MS` |
| `journeys` | bekleme düğümleri aynı anda doluyor | replika artır |
| `maintenance` | retention/rollup uzun sürüyor | tek eşzamanlı; iş süresine bak (`queue_job_duration_seconds{queue="maintenance"}`) |
## 2. Hızlı kapasite
```bash
# compose: ikinci dispatch worker'ı (aynı kuyruklar, Redis adil dağıtır)
docker compose --env-file .env.prod -f infra/compose/docker-compose.prod.yml up -d --scale worker-dispatch=2
# K8s: HPA/KEDA (infra/k8s/pushizo values: worker.groups[].hpa / keda)
kubectl -n pushizo scale deploy/pushizo-worker-dispatch --replicas=4
```text
Sınırlar: Postgres bağlantısı (worker başına `min(DATABASE_POOL_MAX,5)`), sağlayıcı hız sınırı (app başına 5000/sn), `APP_DISPATCH_CONCURRENCY=4`.
Tek kiracı diğerlerini boğamaz (fair scheduling semaforu); yine de dev kampanya bekliyorsa bu beklenen davranıştır.
## 3. Takılı / kayıp işler
- `stalled` logları: lock 60 sn, `maxStalledCount=2` → iş `failed` olur. Sık görülüyorsa işleyici bloklanıyor (DB kilidi, sağlayıcı timeout).
- Redis kaybı/temizlenmesi: `pushizo_deliveries_pending` yüksek, kuyruk boş → `npm run queue:recover` (`restore.md §5`).
- `queued` kalmış mesaj (audience job'u kayıp): `reconcile-deliveries` 15 dk sonra toplar; acilse `queue:recover` audience job'unu da ekler.
## 4. DLQ (failed set) inceleme ve yeniden deneme
Öncelikli yol: **Admin paneli → Sistem sağlığı → kuyruk tablosunda "Başarısız" sayısına tıklayın.** Son 100 başarısız iş (ad, hata, deneme, zaman; gövde gösterilmez) listelenir; tek tek veya toplu "Yeniden dene" (en fazla 1000) yapılabilir. API: `GET /admin/v1/queues/{kuyruk}/failed`, `POST /admin/v1/queues/{kuyruk}/failed/retry { job_id? }` (platform admin + MFA; denetim kaydına `admin.queue_retry` yazılır).
Komut satırından:
```bash
# son 20 başarısız iş (neden + veri)
docker compose … exec redis redis-cli -a "$REDIS_PASSWORD" --no-auth-warning zrevrange hs:dispatch-ios:failed 0 19
docker compose … exec redis redis-cli -a "$REDIS_PASSWORD" --no-auth-warning hgetall "hs:dispatch-ios:<jobId>"
```text
- Teslimat işleri idempotenttir: yeniden kuyruklamak için `queue:recover` yeterlidir (dispatch yalnızca `pending` satırları gönderir).
- Kalıcı hatalar (`RenderError`, geçersiz içerik) için mesaj `failed` kapatılır; müşteriye dönüş.
- `failed` seti 5000 kayıt tutar (`removeOnFail`); eski kayıtları temizlemek: `zremrangebyrank hs:<q>:failed 0 -1001`.
## 5. Önleme
- Kampanya başlangıç gecikmesi SLO (send_at → ilk gönderim < 60 s) için scheduler 1 eşzamanlı; büyük kampanyaları aynı dakikaya zamanlamayın.
- Kota: plan limitleri (`HS_QUOTA_EXCEEDED`) kötüye kullanımı sınırlar; yeni org 24 sa/1000 mesaj.
- Yük testi hedefleri ve kapasite planı: `tests/load/README.md`.Runbook: Sağlayıcı kesintisi (APNs / FCM / Web Push / SES / Resend / Twilio / Netgsm)
Geçici olarak tek kanalı durdurmak gerekirse (örn. sağlayıcı yanlış pozitif/tekrar gönderiyor): worker-dispatch servisinin WORKER_QUEUES değerinden ilgili kuyruğu çıkar…
Runbook: Veri anahtarı (DEK) rotasyonu
Sağlayıcı kimlik bilgileri (APNs p8, FCM private_key, VAPID private key) envelope encryption ile saklanır (ADR-0008): MASTER_KEY yalnızca veri anahtarlarını (DEK) sarar; her kayıt aktif DEK ile v2.<kid>.<iv>.<tag>.<ct>…