EyvaX DevOps

Incident Response Playbook

Step-by-step guide for handling production incidents across the EyvaX platform. Designed for both human operators and automated L1 DevOps agents.

---|-----|---------| | PagerDuty | bluesemi.pagerduty.com | Incident management | | Sentry | sentry.io/bluesemi-qv | Error tracking | | ArgoCD | argocd.eyva.io | Deployment management | | GCP Console | console.cloud.google.com | GKE cluster management |


Severity Levels

Level Description Response Time Examples
P1 Complete outage, all users affected < 15 min Service down, data loss, all pods CrashLoopBackOff
P2 Major degradation, many users affected < 30 min High error rate (>5%), stuck rolling update, circuit breaker open
P3 Minor degradation, some users affected < 2 hours Single feature broken, one service degraded
P4 Minimal impact Next business day Config warnings, Sentry noise, non-critical metrics

Alert Routing Table

When an alert fires, use this table to determine which runbook section to consult and whether auto-resolution is possible.

Alert Name Service Severity Runbook Section Auto-Resolvable
BackendServerHighErrorRate backend-server critical backend-server.md#circuit-breaker-patterns No — investigate root cause first
BackendServerHighErrorRateWarning backend-server warning backend-server.md#circuit-breaker-patterns Maybe — monitor, may self-resolve
BackendServerHighLatency backend-server critical backend-server.md#performance-issues Maybe — check if AI provider is slow
BackendServerHighLatencyWarning backend-server warning backend-server.md#performance-issues Maybe — monitor for 5 min
BackendServerDown backend-server critical backend-server.md#deployment--rollback No — escalate P1
BackendServerMongoDBConnectionHigh backend-server warning backend-server.md#database-issues No — investigate connection pool
AIServiceHighErrorRate ai-service critical ai-service.md#502-errors-and-cascading-failures Maybe — rolling restart if OOMKilled
AIServiceHighLatency ai-service critical ai-service.md#performance-issues Maybe — check resource usage
AIServiceHighLatencyWarning ai-service warning ai-service.md#performance-issues Maybe — monitor, HPA may scale
AIServiceModelInferenceSlowCritical ai-service critical ai-service.md#model-inference-issues No — investigate model performance
AIServiceDown ai-service critical ai-service.md#deployment--rollback No — escalate P1
AIServiceLowPredictionConfidence ai-service warning ai-service.md#signal-processing-errors No — needs model investigation
DataPipelineHighConsumerLag data-pipeline critical data-pipeline.md#high-consumer-lag Maybe — restart if lag > 10k and growing
DataPipelineHighConsumerLagWarning data-pipeline warning data-pipeline.md#high-consumer-lag Maybe — monitor, may be catching up
DataPipelineSevereConsumerLag data-pipeline critical data-pipeline.md#high-consumer-lag No — escalate P1, lag > 10k
DataPipelineStaleData data-pipeline critical data-pipeline.md#data-quality-issues No — investigate data flow
DataPipelineStaleDataWarning data-pipeline warning data-pipeline.md#data-quality-issues Maybe — check consumer health
DataPipelineConsumerDown data-pipeline critical data-pipeline.md#kafka-connection-failure No — escalate P1
DataPipelineClickHouseInsertFailures data-pipeline critical data-pipeline.md#clickhouse-insert-failures No — investigate ClickHouse
EMDGlucoseHighErrorRate emd-glucose critical emd-glucose.md#signal-processing-errors Maybe — check Redis first
EMDGlucoseDown emd-glucose critical emd-glucose.md#startup--health-check-issues No — escalate P1
EMDGlucoseOOMRisk emd-glucose warning emd-glucose.md#memory--resource-issues Yes — rolling restart
EMDGlucoseHighLatency emd-glucose warning emd-glucose.md#signal-processing-errors Maybe — check process pool
HighCPUUsage infrastructure warning Investigate top pods across namespaces No — needs analysis
HighMemoryUsage infrastructure warning Investigate top pods across namespaces No — needs analysis
DiskSpaceLow infrastructure warning Check PVCs and node disk No — needs analysis
DiskSpaceCritical infrastructure critical Check PVCs and node disk No — escalate P2

Cross-Service Dependency Map

Service Architecture

Mobile Apps / Web
      │
      ▼
┌─────────────────────┐
│  eyvax-backend      │ ── MongoDB (Atlas)
│  (eyvax-backend)    │ ── Redis (sidecar)
│  Port: 8000         │ ── ClickHouse
└────────┬────────────┘
         │ calls for AI insights
         ▼
┌─────────────────────┐
│  eyvax-ai           │ ── MongoDB
│  (eyvax-ai)         │ ── Redis (sidecar)
│  Port: 8045         │
└────────┬────────────┘
         │ called by data-pipeline for vital sign computation
         │
┌────────┴────────────┐      ┌─────────────────────┐
│  eyvax-data-pipeline│      │  eyvax-emd-glucose   │
│  (eyvax-data)       │─────▶│  (eyvax-glucose)     │
│  Ports: 8000, 8080  │      │  Port: 8000          │
└─────────────────────┘      └──────────────────────┘
         │                            │
         ▼                            ▼
    Kafka (RedPanda)            Redis (standalone)
    ClickHouse                  Eyva API, Juno API

Cascade Triage Order

When multiple services alert simultaneously, investigate in dependency order (upstream first):

  1. eyvax-ai — shared dependency for data-pipeline AND backend-server. If eyvax-ai is down, both downstream services will show errors.
  2. emd-glucose — called by data-pipeline for glucose metric computation. If eyvax-ai is healthy but emd-glucose is down, data-pipeline glucose metrics will fail.
  3. data-pipeline — if upstream services are healthy, investigate Kafka/ClickHouse connectivity.
  4. backend-server — usually a downstream victim, not root cause. If all other services are healthy, investigate MongoDB/auth.

Decision Tree: Multiple Services Alerting

IF eyvax-ai alerting AND (data-pipeline OR backend-server) also alerting:
  → Root cause is almost certainly eyvax-ai
  → Fix eyvax-ai FIRST — see ai-service.md
  → Other alerts will auto-resolve once eyvax-ai recovers
  → Wait 5 min after eyvax-ai fix, then check if other alerts clear

IF data-pipeline alerting AND emd-glucose alerting BUT eyvax-ai healthy:
  → Root cause is likely emd-glucose (data-pipeline calls it)
  → Fix emd-glucose FIRST — see emd-glucose.md
  → Data-pipeline glucose metric errors will clear after

IF data-pipeline alerting BUT eyvax-ai AND emd-glucose healthy:
  → Isolated data-pipeline issue
  → Check Kafka connectivity and consumer health
  → Check ClickHouse connectivity
  → See data-pipeline.md

IF backend-server alerting BUT all other services healthy:
  → Isolated backend issue
  → Check MongoDB connectivity (Atlas status)
  → Check for recent deployment (rollback candidate?)
  → Check rate limiting (traffic spike?)
  → See backend-server.md

IF all services alerting simultaneously:
  → Likely infrastructure issue (node failure, network partition)
  → Check: kubectl get nodes
  → Check: kubectl get pods --all-namespaces | grep -v Running
  → Escalate P1

Auto-Resolution Safety Boundaries

Safe to Auto-Execute (No Human Approval Needed)

Action Conditions Cooldown
Rolling restart of a deployment Pod in OOMKilled or restart count < 3 Max once per 30 min per service
Acknowledge PagerDuty incident Always safe None
Resolve PagerDuty incident After confirming service health endpoint returns 200 None
Resolve Sentry issues Confirmed stale (last event > 1 hour ago) None
Reset a circuit breaker Via admin API, only if underlying dependency is healthy Max once per 10 min per breaker
Scale down stuck old ReplicaSet During rolling update, new RS healthy and serving Once per rolling update
Restart Redis sidecar/standalone Redis pod not Running Max once per 30 min

Requires Human Approval

Escalation Triggers (Stop and Page P2)


When You Get Paged

1. Acknowledge (< 5 min)

# Acknowledge via PagerDuty API
curl -s -X PUT "https://api.pagerduty.com/incidents" \
  -H "Authorization: Token token=$PAGERDUTY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "From: $PAGERDUTY_EMAIL" \
  -d '{
    "incidents": [{"id": "<INCIDENT_ID>", "type": "incident_reference", "status": "acknowledged"}]
  }'

2. Assess (< 10 min)

Check all service health endpoints:

# Backend Server
curl -s https://api.eyvax.com/health | python3 -m json.tool
# Expected 200: {"status": "healthy", "checks": {"database": {"status": "healthy"}, "database_write": {"status": "healthy"}}}
# Unhealthy 503: {"status": "unhealthy", "checks": {"database": {"status": "unhealthy", "type": "mongodb"}}}

# AI Service
curl -s https://eyvax-ai.eyva.io/health | python3 -m json.tool
# Expected 200: {"status": "healthy", "dependencies": {...}}

# EMD Glucose
curl -s https://emd-glucose.eyva.io/health | python3 -m json.tool
# Expected 200: {"status": "healthy", "checks": {"configuration": {"healthy": true}, "external_api": {"healthy": true}, "caching": {"healthy": true}}}
# Unhealthy 503: checks will show which dependency is failing

# Data Pipeline (internal only — not exposed via ingress)
# Check via kubectl port-forward or pod exec
kubectl exec deploy/eyvax-sensor-raw-data-processor -n eyvax-data -- curl -s http://localhost:8080/health | python3 -m json.tool
# Expected 200: {"status": "healthy", "checks": [{"name": "kafka", "status": "healthy"}, {"name": "clickhouse", "status": "healthy"}]}
# Degraded 503: {"status": "degraded"|"unhealthy", "checks": [...]} — check which check failed

Check pods across all namespaces:

# Set cluster context
gcloud container clusters get-credentials eyvax-dev-zone --region asia-south1 --project eyva-440707

# Quick status across all services
for ns in eyvax-backend eyvax-ai eyvax-glucose eyvax-data; do
  echo "=== $ns ==="
  kubectl get pods -n $ns -o wide 2>/dev/null
done

# Expected healthy output per pod:
# NAME                                    READY   STATUS    RESTARTS   AGE
# eyvax-backend-server-xxx-yyy            2/2     Running   0          2d
#
# Unhealthy indicators:
# STATUS = OOMKilled → memory limit exceeded, needs restart
# STATUS = CrashLoopBackOff → application crashing, check logs
# STATUS = ImagePullBackOff → image not found in registry, check CI
# READY = 0/2 → container not passing readiness probe
# RESTARTS > 3 → pod is crash-looping, check describe and logs

Check recent deployments (was something just deployed?):

# ArgoCD sync status for all apps
for app in eyvax-backend-server eyvax-ai eyvax-emd-glucose eyvax-data-pipeline; do
  STATUS=$(kubectl get application $app -n argocd -o jsonpath='{.status.sync.status} {.status.health.status}' 2>/dev/null)
  echo "$app: $STATUS"
done

# Expected: "Synced Healthy" for each app
# "OutOfSync" → ArgoCD hasn't applied latest manifest changes
# "Degraded" → pods not healthy after sync
# "Progressing" → rollout in progress

# Check if a deployment happened in the last hour
for ns in eyvax-backend eyvax-ai eyvax-glucose eyvax-data; do
  echo "=== $ns ==="
  kubectl rollout history deployment -n $ns 2>/dev/null | tail -5
done

Check Sentry for errors across all projects:

# Sweep all Sentry projects for unresolved issues
for project in eyvax-backend-server eyvax-ai eyvax-emd-glucose eyvax-data-pipeline; do
  echo "=== $project ==="
  curl -s -H "Authorization: Bearer $SENTRY_API_KEY" \
    "https://sentry.io/api/0/projects/bluesemi-qv/$project/issues/?query=is:unresolved&sort=date&statsPeriod=1h&limit=5" | \
    python3 -c "
import json, sys
try:
  issues = json.load(sys.stdin)
  if isinstance(issues, list):
    if not issues:
      print('  No unresolved issues')
    else:
      for i in issues:
        print(f'  [{i[\"level\"]}] {i[\"count\"]}x | {i[\"title\"][:80]} | Last: {i[\"lastSeen\"][:19]}')
  else:
    print(f'  API error or project not found')
except Exception as e:
  print(f'  Parse error: {e}')
"
done

# NOTE: emd-glucose events may appear in eyvax-backend-server project
# due to shared Sentry DSN. Check server_name tag to identify source pod.

Check PagerDuty for open incidents:

curl -s -H "Authorization: Token token=$PAGERDUTY_API_KEY" \
  "https://api.pagerduty.com/incidents?statuses[]=triggered&statuses[]=acknowledged&limit=25" | \
  python3 -c "
import json, sys
data = json.load(sys.stdin)
incidents = data.get('incidents', [])
if not incidents:
  print('No open incidents')
else:
  for i in incidents:
    print(f'  [{i[\"status\"]}] {i[\"title\"][:80]} | ID: {i[\"id\"]} | Created: {i[\"created_at\"][:19]}')
"

3. Communicate

For P1/P2 incidents:

Post in #incidents:

INCIDENT IN PROGRESS
- Service: [SERVICE_NAME]
- Impact: [DESCRIPTION]
- Status: Investigating
- IC: [YOUR_NAME]
- Next update: [TIME]

Escalate if needed:

4. Mitigate

Common quick fixes:

Symptom Quick Fix Command
High error rate after deploy Rollback kubectl rollout undo deployment/<name> -n <namespace>
High latency, pods at CPU limit Wait for HPA Check kubectl get hpa -n <namespace> — scaling in progress?
OOMKilled pods Rolling restart kubectl rollout restart deployment/<name> -n <namespace>
Kafka consumer lag growing Restart consumer kubectl rollout restart deployment/eyvax-sensor-raw-data-processor -n eyvax-data
MongoDB connection errors Check Atlas, restart Check Atlas, then kubectl rollout restart deployment/eyvax-backend-server -n eyvax-backend
Circuit breaker OPEN Reset breaker curl -X POST https://api.eyvax.com/admin/circuit-breakers/{name}/reset
Redis not responding Restart Redis kubectl rollout restart deployment/<redis-deployment> -n <namespace>
ArgoCD OutOfSync Force sync argocd app sync <app-name> --force
Stuck rolling update Check events kubectl describe deployment/<name> -n <namespace> — look for probe failures

Rollback commands (K8s):

# Rollback to previous revision
kubectl rollout undo deployment/eyvax-backend-server -n eyvax-backend

# Check rollback status
kubectl rollout status deployment/eyvax-backend-server -n eyvax-backend
# Expected: "deployment "eyvax-backend-server" successfully rolled out"
# Stuck: "Waiting for deployment ... rollout to finish: X out of Y new replicas have been updated..."

# ArgoCD rollback (if deployed via ArgoCD)
argocd app history eyvax-backend-server
argocd app rollback eyvax-backend-server <revision-number>

5. Resolve

Verify resolution:

# 1. Check health endpoint returns 200
curl -s -o /dev/null -w "%{http_code}" https://api.eyvax.com/health
# Expected: 200

# 2. Bulk-resolve PagerDuty incidents
# First, list open incidents and get their IDs
INCIDENT_IDS=$(curl -s -H "Authorization: Token token=$PAGERDUTY_API_KEY" \
  "https://api.pagerduty.com/incidents?statuses[]=triggered&statuses[]=acknowledged&limit=50" | \
  python3 -c "
import json, sys
data = json.load(sys.stdin)
incidents = data.get('incidents', [])
payload = [{'id': i['id'], 'type': 'incident_reference', 'status': 'resolved'} for i in incidents]
print(json.dumps({'incidents': payload}))
")

# Then resolve them all at once
curl -s -X PUT "https://api.pagerduty.com/incidents" \
  -H "Authorization: Token token=$PAGERDUTY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "From: $PAGERDUTY_EMAIL" \
  -d "$INCIDENT_IDS"

# 3. Bulk-resolve stale Sentry issues (optional)
# Resolve all unresolved issues in a project where last event was > 1 hour ago
curl -s -X PUT -H "Authorization: Bearer $SENTRY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "resolved"}' \
  "https://sentry.io/api/0/projects/bluesemi-qv/<project>/issues/?query=is:unresolved+lastSeen:-1h"

Resolution message template:

INCIDENT RESOLVED
- Service: [SERVICE_NAME]
- Duration: [X] minutes
- Root cause: [BRIEF DESCRIPTION]
- Resolution: [WHAT FIXED IT]
- Postmortem: [Yes/No - scheduled for DATE]

Service-Specific Runbooks

Service Runbook Health URL Namespace Common Issues
Backend Server backend-server.md https://api.eyvax.com/health eyvax-backend Auth failures, MongoDB, circuit breakers, rate limiting
AI Service ai-service.md https://eyvax-ai.eyva.io/health eyvax-ai 502 cascading, OOMKilled, model inference, memory
Data Pipeline data-pipeline.md Internal: localhost:8080/health eyvax-data Consumer lag, ClickHouse inserts, stale data, CronJobs
EMD Glucose emd-glucose.md https://emd-glucose.eyva.io/health eyvax-glucose Cache warmup, Redis, OOMKilled, signal processing

Escalation Matrix

Time in Incident Action
0 min On-call acknowledges, begins assessment
15 min If no progress, escalate to secondary on-call
30 min If P1, involve engineering lead
60 min If P1, involve management

Emergency contacts:


After the Incident

P1/P2 Incidents Require:

  1. Postmortem within 48 hours (template)
  2. Blameless review meeting within 1 week
  3. Action items tracked to completion

Metrics to Track:


SLO Targets Reference

Service Availability P95 Latency P99 Latency Error Rate Error Budget (30d)
backend-server 99.9% < 500ms < 1000ms < 0.1% 43.2 min
ai-service 99.9% < 2000ms < 5000ms < 0.1% 43.2 min
data-pipeline 99.9% < 1000ms (processing) — < 0.1% (inserts) 43.2 min
emd-glucose 99.9% < 5000ms < 15000ms < 0.1% 43.2 min