Passa al contenuto principale

DR Restore Drill - Akira

Esercitazione disaster recovery per validare che i backup PostgreSQL/TimescaleDB siano ripristinabili e che RPO/RTO osservati restino entro i target dichiarati.

Questo runbook copre il drill periodico su VM isolata. Per incidenti reali usare anche docs/runbooks/dr-restore-procedure.md.

Scope

  • PostgreSQL 16 + TimescaleDB restore da pg_dump --format=custom.
  • Hypertable cdr, tabelle applicative, ENUM e funzioni critiche.
  • Misura RTO: provisioning VM, download dump, restore, sanity check.
  • Misura RPO: differenza tra failure time e timestamp backup, oppure confronto con sorgente live se configurata nello script.
  • Redis: recovery best-effort da stato ricostruibile; cache, idempotency keys e rate limit non sono source of truth.
  • NATS JetStream: verifica backlog separata; restore dello stato JetStream resta low priority e fuori dallo script del drill.

Pre-requisiti

  • Accesso Hetzner Cloud con hcloud e HCLOUD_TOKEN.
  • VM scratch on-demand, default akira-dr-scratch in fsn1.
  • SSH key Hetzner gia' registrata, default akira-staging.
  • Storage Box BX11 raggiungibile via SFTP.
  • Dump PostgreSQL in /backups/pg/akira-*.dump.
  • infra/cloud-init/dr-scratch.yml disponibile sul runner.
  • Tool locali: bash, hcloud, ssh, scp, sftp, jq, date, curl opzionale per notifiche.

Target

Target canonici Fase 1 da ADR-0012:

AmbienteRPORTO
Staging5 minuti15 minuti
Produzione futurada confermareda confermare

Target pilot operativo per TASK-200:

MetricaTarget
Provisioning VM scratch< 10 minuti
Restore dump staging< 2 ore
RTO totale drill< 4 ore
RPO massimo pilot< 1 ora

Se ADR e task divergono, prevale ADR-0012. Il drill TASK-200 mantiene i target pilot piu' larghi solo come soglie reportistiche.

Procedura Step-by-step

1. Preparare baseline e failure time

Scegliere il timestamp simulato dell'incidente. Se non impostato, lo script usa l'ora di avvio del drill.

export DR_FAILURE_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"

Opzionale: configurare confronto con database sorgente live per calcolare delta CDR e RPO sui dati reali.

export DR_SOURCE_DB_HOST=akira-db-01-staging
export DR_SOURCE_DB_NAME=akira

2. Eseguire il drill automatizzato

HCLOUD_TOKEN=... \
STORAGEBOX_HOST=... \
STORAGEBOX_USER=... \
STORAGEBOX_SSH_KEY=/root/.ssh/storagebox_ed25519 \
DR_SSH_KEY_NAME=akira-staging \
./scripts/dr-restore-drill.sh

Lo script:

  1. seleziona l'ultimo dump akira-*.dump su Storage Box;
  2. crea VM scratch Hetzner con cloud-init PostgreSQL/TimescaleDB;
  3. scarica il dump via SFTP;
  4. carica dump e helper sanity sulla VM;
  5. ripristina in database isolato;
  6. esegue sanity check;
  7. scrive reports/<drill-id>/REPORT.md;
  8. distrugge la VM, salvo DR_KEEP_VM=1.

3. Verificare integrita'

Il drill e' valido solo se il report mostra:

  • tables_count >= 70;
  • enums_count >= 20;
  • hypertable_cdr_exists = true;
  • fn_resolve_agent_fee_exists = true;
  • pg_restore completato senza exit code non-zero.

Per debug manuale sulla VM scratch:

sudo -u postgres psql -d akira_dr_test -c \
"SELECT COUNT(*), MIN(call_start_ts), MAX(call_start_ts) FROM cdr;"
sudo -u postgres psql -d akira_dr_test -c \
"SELECT hypertable_name, num_chunks FROM timescaledb_information.hypertables;"

Se il modello CDR usa un nome colonna diverso da call_start_ts, usare il JSON del sanity helper come fonte minima e registrare la discrepanza nel report.

4. Misurare RPO/RTO

  • RTO provisioning = vm_ready_ts - drill_start_ts.
  • RTO restore = restore_complete_ts - restore_start_ts.
  • RTO totale = sanity_complete_ts - drill_start_ts.
  • RPO da backup = failure_ts - backup_ts.
  • RPO da confronto live, se configurato = source_latest_cdr_ts - restored_latest_cdr_ts.

Per drill mensili senza sorgente live, il valore autoritativo e' l'RPO da timestamp dump/WAL policy, non il delta CDR.

5. Cleanup

Lo script elimina la VM a fine esecuzione. Se e' stato usato DR_KEEP_VM=1, eliminarla manualmente dopo il debug:

hcloud server delete akira-dr-scratch

Notifiche

Impostare DRILL_NOTIFY_WEBHOOK per inviare l'esito a Slack o webhook compatibile:

export DRILL_NOTIFY_WEBHOOK=https://hooks.slack.com/services/...

Il payload contiene drill id, verdict, RTO, RPO e path report.

Cadence

  • Pilot: mensile, primo giorno del mese alle 04:00 UTC.
  • GA: trimestrale, salvo diversa decisione ADR.
  • Post-incidente: drill ad-hoc entro 2 settimane dalla RCA.

Postmortem Template

Per drill fallito o degradato registrare:

  • data e drill id;
  • dump selezionato;
  • RPO/RTO osservati;
  • sanity JSON;
  • log pg_restore;
  • causa del fallimento;
  • remediation owner e scadenza;
  • decisione su retry.

Se disponibile, usare docs/runbooks/postmortem-template.md; altrimenti appendere una nota strutturata in docs/runbooks/_drill-log.md.