RRelo Docs
API & GuíasGuías

Recovery y operaciones

Guía umbrella para recuperación de datos, failover, DLQ, backfills y housekeeping operativo de Relo.

Recovery y operaciones

Esta guía agrupa los procedimientos que usamos cuando algo sale mal o hay que re-procesar datos históricos. Cubre R2 WAL replay, DLQ recovery, failover entre ingest paths, backfills de reglas, limpieza de datos de QA y rotación de tokens.

Diagrama interactivo
Cargando diagrama…

Antes de empezar Si no estás seguro del alcance, revisa primero System health para ver el estado actual de pipelines, DLQ y componentes.

Chequeo de salud del pipeline

El servicio Go expone un endpoint único de salud. Llámalo desde cualquier lado a través del tunnel o directo en el servidor:

# A través del Cloudflare Tunnel
curl -s https://ingest.relo.mx/health | jq

# Directo en el servidor Hetzner
ssh root@178.156.195.219
curl -s http://localhost:4080/health | jq

Campos clave

CampoSano cuando...Rojo cuando...
buffer_events0 (todo descargado a CH)Número que sube sostenidamente.
dlq_depth {clicks,pixel,s2s}Todo en ceroCrece persistentemente.
last_pull_atDentro de la última horaEstá viejo.
last_pull_stats.errors0Errores > 0 o cero purchases en horario hábil.
failover.activefalsetrue (hay failover activo).
recovery.health_check{clickhouse_ok,r2_wal_ok,supabase_ok,dragonfly_ok} todo trueAlguno es false.

Baseline sano: buffer_events: 0, dlq_depth todo cero, last_pull_at reciente, last_pull_stats.errors: 0, failover.active: false y los cuatro flags de health check en true.

Redes de seguridad de la ingesta

Entender estas capas hace que cada acción de recovery sea segura de re-ejecutar:

  • AppsFlyer Pull API cada hora. Deduplica por sale_hash; re-jalar cualquier ventana nunca duplica conteos.
  • Write-ahead log en R2. Cada lote se escribe a R2 antes del insert en CH.
  • ReplacingMergeTree. Re-insertar el mismo event_id colapsa en el siguiente merge.
  • Loops de DLQ. Las fallas en rutas vivas caen a DLQ y se re-procesan automáticamente.

R2 WAL replay

Relo escribe cada evento primero en un write-ahead log (WAL) en Cloudflare R2 antes de intentar insertarlo en ClickHouse. Esto significa que, si ClickHouse rechaza un batch o el buffer se desborda, los eventos originales siguen guardados en R2.

¿Cuándo usarlo?

  • ClickHouse estuvo caído y eventos llegaron al backbone.
  • El buffer de CH se desbordó (WAL replay needed en System health).
  • Recibiste una alerta de R2 WAL recovery o ves faltante de eventos recientes.

Layout de R2 WAL

relo-event-wal/
├── wal/events/YYYY/MM/DD/HH/{ulid}.ndjson.gz
├── wal/clicks/YYYY/MM/DD/HH/{ulid}.ndjson.gz
└── receipts/{kind}/YYYY/MM/DD/{ulid}.json

Replay rápido por API (últimas horas)

El backbone expone un endpoint administrativo para re-encolar eventos del WAL al writer de ClickHouse:

curl -X POST "https://ingest.relo.mx/admin/wal-replay?hours=6" \
  -H "Authorization: Bearer $ADMIN_TOKEN"
ParámetroDefaultMáximoUso
hours648Ventana hacia atrás desde ahora.

La respuesta indica cuántos archivos escaneó, cuántos eventos re-encoló y errores por archivo. Es idempotente: la tabla events es ReplacingMergeTree con event_id, así que re-insertar eventos ya presentes colapsa en el merge.

Replay masivo por CLI (disaster recovery)

Para reconstruir ClickHouse desde cero o para un rango de días específico, usa el comando replay en backbone/cmd/replay:

cd backbone
go build -o bin/replay ./cmd/replay

export CF_ACCOUNT_ID="..."
export CF_API_TOKEN="..."
export CF_R2_WAL_BUCKET="relo-event-wal"
export CH_ADDR="127.0.0.1:9000"
export CH_DATABASE="relo"

# Listar archivos sin insertar (dry-run)
./bin/replay --from 2026-07-01 --to 2026-07-09 --dry-run

# Reproducir eventos de un cliente específico
./bin/replay --from 2026-07-01 --to 2026-07-09 --client 3

# Reproducir clicks
./bin/replay --from 2026-07-01 --to 2026-07-09 --prefix wal/clicks

Verificación diaria del WAL

El propio R2WAL puede verificar que los archivos recientes sean parseables. Úsalo en un cron operativo para detectar corrupción silenciosa:

Diagrama interactivo
Cargando diagrama…

AppsFlyer re-pull (Recovery A)

Usa esto cuando el hueco es de data originada en AppsFlyer (compras jaladas de AF). Como el pull deduplica por sale_hash, es seguro re-ejecutar cualquier ventana.

Detectar el hueco

ssh root@178.156.195.219 "clickhouse-client -d relo -q \
  \"SELECT toDate(event_time) AS d, count() AS n
    FROM relo.events
    WHERE event_time >= now() - INTERVAL 14 DAY
    GROUP BY d ORDER BY d\""

Una caída inesperada en el conteo diario vs días vecinos es la señal.

Disparar pull manual

curl -X POST https://ingest.relo.mx/pull/trigger \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"from":"2026-05-28T00:00:00Z","to":"2026-05-30T00:00:00Z"}'

Respuesta típica:

{"ok":true,"status":"started","note":"pull running in background — watch /health last_pull_at"}

Jala ventanas estrechas (2–3 días a la vez). Un rango enorme inunda el buffer de CH de golpe. Recorre huecos largos en pulls cortos y observa buffer_events bajar a 0 entre cada uno.

Leer ADMIN_TOKEN del servidor

Para no pegar el secreto en tu terminal local:

ssh root@178.156.195.219
ADMIN_TOKEN=$(systemctl show relo-ingest -p Environment \
  | tr ' ' '\n' | grep '^ADMIN_TOKEN=' | cut -d= -f2-)

curl -X POST http://localhost:4080/pull/trigger \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"from":"2026-05-28T00:00:00Z","to":"2026-05-30T00:00:00Z"}'

Nota de escalación

La Pull API de AppsFlyer tiene una ventana de lookback limitada. Si un re-pull reporta cero filas para una fecha que sabes que tuvo ventas, la data ya salió del rango servible de AF. Exporta la ventana desde el dashboard de AppsFlyer y reconcilia manualmente.

DLQ recovery

Cuando el backbone no puede procesar un evento (ClickHouse caído, regla rota, payload inválido), lo escribe a un dead letter queue (DLQ) en R2 para no perderlo. Los contadores de profundidad de DLQ se ven en System health → Data pipelines.

Tipos de DLQ

TipoTrigger típicoRuta aproximada en R2
ClicksKV caído o write fallidodlq/clicks/...
S2S postbacksPostback inválido o CH caídodlq/s2s/...
PixelPixel sin cid o evento mal formadodlq/pixel/...

Flujo de recuperación

Diagrama interactivo
Cargando diagrama…

Re-procesar desde el backbone

El monitor de salud del backbone rastrea la profundidad de DLQ (dlqClicksPending, dlqS2SPending, dlqPixelPending) y decrementa el contador cuando se recuperan eventos. Si la causa fue una caída temporal, reiniciar o re-encolar el procesamiento suele ser suficiente.

Para re-proceso manual, consulta el equipo de infra; típicamente implica:

  1. Listar archivos del prefijo DLQ correspondiente en R2.
  2. Validar un sample para descartar payloads rotos.
  3. Re-enviar eventos sanos al handler correspondiente o insertarlos directamente a CH.

Failover

La plataforma tiene tres caminos de ingesta que se usan como respaldo mutuo:

Diagrama interactivo
Cargando diagrama…

Estados de failover

El cron externo del API Worker revisa cada hora:

EstadoSignificadoAcción
All operationalBackbone responde status: okNinguna.
Backbone DOWN, VPS UPEventos se bufferizan en VPSInvestigar backbone; VPS mantiene ingest.
Both DOWNSolo R2 DLQ recibe datosCRITICAL: atención inmediata; nada se procesa en tiempo real.

Cómo forzar un health check

curl "https://relo-api.quomx.workers.dev/api/health"
curl "https://ingest.relo.mx/health"
curl "https://upload.relo.mx/health"

Recuperación tras falla total

  1. Restaurar backbone o VPS según corresponda.
  2. Revisar DLQ depth en System health.
  3. Correr R2 WAL replay para los eventos que solo llegaron al WAL.
  4. Re-procesar DLQ si aplica.
  5. Verificar que partner_daily_aggregates se actualiza (commission mirror no vacío).

Deploy y rollback del backbone

Cuando necesites actualizar el servicio Go en Hetzner:

# 1) Build local para Linux AMD64
cd backbone
GOOS=linux GOARCH=amd64 go build -o relo-ingest-new ./cmd/ingest/

# 2) Subir al servidor
scp relo-ingest-new root@178.156.195.219:/opt/relo/relo-ingest-new

# 3) Swap con backup, reiniciar y verificar
ssh root@178.156.195.219 "systemctl stop relo-ingest \
  && cp /opt/relo-ingest/relo-ingest /opt/relo-ingest/relo-ingest.bak \
  && cp /opt/relo/relo-ingest-new /opt/relo-ingest/relo-ingest \
  && chmod +x /opt/relo-ingest/relo-ingest \
  && systemctl start relo-ingest"

# 4) Verificar salud
curl -s https://ingest.relo.mx/health | jq '.status'
ssh root@178.156.195.219 "journalctl -u relo-ingest -f"

Rollback

Si /health no regresa limpio:

ssh root@178.156.195.219 "systemctl stop relo-ingest \
  && cp /opt/relo-ingest/relo-ingest.bak /opt/relo-ingest/relo-ingest \
  && systemctl start relo-ingest"

Backfill de reglas

Cuando cambias o creas una regla de segmento/atribución y quieres que aplique retroactivamente sobre eventos históricos, usa el backfill de reglas.

Diagrama interactivo
Cargando diagrama…

Endpoint

curl -X POST "https://ingest.relo.mx/admin/rules/:id/backfill" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "2026-06-01",
    "to": "2026-07-09",
    "dry_run": true,
    "trigger_recalc": false,
    "event_types": ["af_purchase"]
  }'
CampoDefaultDescripción
fromHace 30 díasInicio del rango (YYYY-MM-DD o RFC3339).
toHoyFin del rango.
dry_runtrueSi true, solo cuenta matches y devuelve muestras; no escribe.
trigger_recalcfalseSi true, dispara recálculo de comisiones afectadas.
event_typesTodosFiltro opcional de tipos de evento.

Límites de seguridad

  • Máximo 90 días de rango.
  • Máximo 50,000 eventos evaluados.
  • Siempre corre dry_run=true primero.

Qué pasa durante el backfill

  1. Se fuerza la regla como active y se ignoran effective_from/to para evaluar el match.
  2. Se escanean eventos de ClickHouse para el cliente y rango.
  3. Los matches se insertan como nueva versión (_version + 1) de los eventos.
  4. Si trigger_recalc=true, se recalculan comisiones para los (partner, año, mes) afectados.

Limpieza de datos de QA

Después de pruebas de postbacks, imports o pixels, es común dejar eventos de prueba mezclados. Límpialos antes de que afecten reportes o pagos.

Identificar datos de prueba

Usa un prefijo consistente para todo QA, por ejemplo TEST_:

  • action_id en postbacks: TEST_12345
  • order_id en CSVs: TEST_ORDER_001
  • Campañas de prueba: nombra con [QA].

Borrar de ClickHouse

ALTER TABLE relo.events DELETE
WHERE client_id = <client_id>
  AND event_properties['action_id'] LIKE 'TEST_%';

Borrar de Supabase

DELETE FROM network_action_statuses
WHERE client_id = <client_id>
  AND action_id LIKE 'TEST_%';

Borrar import de prueba

Si el import quedó registrado en import_files, marca como cancelled o bórralo según política interna:

UPDATE import_files
SET status = 'cancelled', error = 'QA cleanup'
WHERE id = <import_id>;

Rotación de tokens

Rotar tokens cuando:

  • Se compartió una URL con ?key=... por chat/email/público.
  • Un ex-empleado o partner tenía acceso.
  • Hay sospecha de uso no autorizado.
  • Es parte del housekeeping periódico.

Tokens de fuentes externas (Everflow / GA4 / Admitad)

  1. Identifica el id de client_data_sources.
  2. Genera un nuevo token:
    openssl rand -hex 32
  3. Actualiza solo el campo auth_token dentro de extra_config:
    UPDATE client_data_sources
    SET extra_config = jsonb_set(
      extra_config,
      '{auth_token}',
      to_jsonb('NUEVO_TOKEN'::text)
    )
    WHERE id = <source_id>;
  4. Verifica:
    curl "$URL?key=NUEVO_TOKEN"  # 200
    curl "$URL?key=TOKEN_VIEJO"   # 401
  5. Actualiza postbacks/configuraciones del partner con la nueva URL.
  6. Documenta la rotación en el runbook del cliente.

Tokens de upload de partners

Para rotar el Bearer token que un partner usa para subir CSVs al ingest:

curl -X POST "https://relo-api.quomx.workers.dev/api/partners/:id/upload-credentials/rotate" \
  -H "Authorization: Bearer $ADMIN_TOKEN"

API keys de sistema

Las variables de entorno del Worker (SUPABASE_SERVICE_ROLE_KEY, MAILGUN_API_KEY, ANTHROPIC_API_KEY, etc.) se rotan vía wrangler secret put. Después de rotar:

  1. Re-despliega el Worker.
  2. Verifica /api/health.
  3. Revisa que el backbone pueda seguir llamando al Worker (/api/admin/system-alert).

Checklist operativo mensual

TareaHerramienta / comandoFrecuencia
Revisar DLQ depthSystem health → Data pipelinesSemanal
Verificar WAL integrityVerifyRecent vía cronDiaria
Revisar drifts de comisiónSystem health → Commission paritySemanal
Rotar tokens expuestosRunbooks de rotaciónSegún incidente
Limpiar datos de QASQL de limpiezaDespués de cada QA
Revisar alerts del monitorLogs de relo-ingest + MailgunDiaria
Revisar backups de ClickHousebackbone/scripts/backup-clickhouse.shSemanal
Housekeeping de ramasgit branch --merged mainQuincenal

Troubleshooting

SíntomaCausa probableAcción
Eventos faltan en dashboardCH buffer overflowR2 WAL replay
DLQ clicks subeKV caído o write fallidoRevisar DragonflyDB, re-procesar DLQ
Comisiones no cuadran tras regla nuevaNo se recalcularonBackfill + trigger_recalc
Import quedó processing horasQueue message stuckPOST /api/v3/imports/reset-stuck o POST /api/imports/reset-stuck
Mirror de gated commission vacíoAggregator no corrióVerificar partner_daily_aggregates; re-correr aggregator
Backbone alerta CRITICALAmbos paths caídosEscalar infra; todo ingesta a R2 DLQ

Relación con otras guías

  • System health — Salud en tiempo real de todos los componentes.
  • Pipeline — Arquitectura de ingesta y flujo de datos.
  • Monitoring — Alertas y observabilidad.
  • Runbooks de ops — Procedimientos recurrentes (migración de docs, housekeeping).
  • API Reference — Endpoints de debug, imports y admin.

Necesitas ayuda?

Para failover total, restore de backups o rotación de credenciales críticas, contacta al equipo de infraestructura de Relo y documenta todo en el runbook correspondiente.

On this page