Ir al contenido

Deploy de Rolling Databases a producción

Replica en producción el deploy de Rolling Databases después de validarlo en staging (ver Deploy de Rolling Databases a staging). Modelo de 2 flags por-tenant (rollingDb / rollingDbDrop), default off → single-DB legacy (paridad).

Riesgo deploy (todo off): bajo (paridad). Riesgo activación: alto — datos de producción; mitigado por observación + reversibilidad por flag + canario real.

AspectoStagingProducción
Branch socketdevelopmain
Branch sdk (release)tag sobre mastertag sobre master (igual)
Workflow socketrelease-staging.yml (Env staging)release-prod.yml (Env production)
ArgoCD nstables-socket-stagingtables-socket
MigracionesJob in-clusterJob migrate de release-prod.yml o Job in-cluster
DB_PREFIXstagingel actual de prod (no cambiar)
CouchDBcluster stagingcluster prod (max_dbs_open acá)
Canariotenant de pruebatenant real chico, con aviso
  • Staging validado (criterio de salida del runbook de staging).
  • max_dbs_open aplicado en el CouchDB de PROD (bites-infrastructure/kubernetes/couchdb/values.yamlcouchdbConfig.couchdb.max_dbs_open: 5000). Requiere acceso de escritura a infra. Prereq DURO antes de cualquier flag (el rotate prewarmea day-DBs; sin headroom tumba el cluster — ya cayó 2 veces por loops).
  • Reconciliación de bookkeeping de migraciones en PROD (ver abajo, crítico).
  • SDK nuevo (≥1.4.17) publicado y en web/app de prod — sin él, el cutover no llega a los devices (re-bootstrapean a tables-prod).
  • BACKUP_ENABLED != false en prod (confirmar).
  • Ventana de cambio acordada + canal con operaciones. Plan de rollback leído.
  • Branch model: main (socket) = prod. Push a mainrelease-prod.yml: jobs test → migrate → build-and-push (este último gateado por el Environment production) → escribe el tag en k8s/overlays/prod/kustomization.yaml con [skip ci]ArgoCD sincroniza tables-socket (prod).
  • Cluster: contexto kubectl ticketplus/do-production-bites, namespace tables-socket.
  • Env del socket (prod): CONFLICTS_ENABLED=false (hasta habilitar el DROP), AUTO_ARCHIVE_ENABLED=true, ROLLING_DB_MAX_OPEN=5000. DB_PREFIX = el actual, NO cambiar.

Las migraciones nuevas de Rolling DB son seguras de aplicar: 0012_add_day_databases, 0013_add_company_feature_flags, 0014_add_conflict_reviews y 0015_simplify_rolling_db_flags (colapsa las 4 columnas de flags de 0013 en rolling_db + rolling_db_drop; usa ADD/DROP COLUMN IF [NOT] EXISTS → idempotente).

Opción recomendada — sembrar el bookkeeping y luego migrar:

  1. Verificar qué hay (select * from drizzle.__drizzle_migrations order by created_at;).
  2. Si está vacío: sembrar las entradas 0000–0011 como ya aplicadas (sin re-ejecutar SQL), de modo que db:migrate solo aplique 0012 → 0015.
  3. Recién entonces correr db:migrate (Job in-cluster con GIT_REF: main, o el job migrate de release-prod.yml con MIGRATION_DATABASE_URL).

Opción alternativa — aplicar las migraciones nuevas a mano: como company_feature_flags es nueva, basta crear la tabla con el schema final de 2 columnas (rolling_db, rolling_db_drop) + day_databases + conflict_reviews, y registrar 0012–0015 en el bookkeeping (evita el create-de-4-cols-luego-drop de aplicar 0013+0015).

Validar: tablas day_databases, company_feature_flags (con rolling_db/rolling_db_drop), conflict_reviews existen; __drizzle_migrations con 0015 última; ninguna tabla/dato existente alterado.

Orden de deploy en producción (TODO en OFF)

Sección titulada «Orden de deploy en producción (TODO en OFF)»
  1. Pre-merge: pre-requisitos cumplidos. Migración de prod planificada.

  2. Publicar el SDK (master) + promover web/app: bump + tag del SDK sobre master → publish (Env production). web/app a sus ramas de prod consumiendo la versión publicada. Validar: con flags off, device CORE-only (paridad).

  3. Migrar el Postgres de PROD: aplicar solo 0012 → 0015 (sembrando bookkeeping o a mano). Validar: tablas creadas con el schema de 2 flags; sin tocar datos existentes.

  4. Promover socket developmain: dispara release-prod.yml. El job migrate debe ser no-op si ya migraste (idempotente). build-and-push queda pendiente del Environment production → aprobar tras confirmar la migración. Validar (todo OFF):

    • GET /admin/rolling-db/{cluster,tenants} con sys_admin → 200; sin rol → 403. GET /admin/crons → 200.
    • Logs prod: day-db-cron agendado, rotate-cycle 0 tenant(s) rolling-enabled.
    • Tenants reales operan idéntico a hoy (single-DB, invalidate_daily, archive legacy) → paridad. New Relic sin regresiones. /health → 200.
  5. Deploy consola backoffice (prod) (opcional, para observar): validar /cluster + /tenants reales contra prod.

Activación por-tenant en producción (canario real)

Sección titulada «Activación por-tenant en producción (canario real)»
  1. Encender rollingDb en el canario:

    Ventana de terminal
    PATCH /admin/rolling-db/flags/<companyId> body: { "rollingDb": true }
  2. Gatillar el rotate en ventana de bajo tráfico:

    Ventana de terminal
    POST /admin/crons/rolling-db-rotate/run # responde 202; cluster-lock protege contra doble-run

    Esto crea las day-DBs + warmea CORE + invalida el tenant → TODOS sus devices destruyen su PouchDB y re-descargan el catálogo desde CORE (pico de re-bootstrap). Para tenants grandes, hacerlo en ventana de bajo tráfico y monitorear /bootstrap + CouchDB.

  3. Verificar (observación más larga que en staging):

    • day-DBs PREWARMED/ACTIVE; ..._core con catálogo (doc_count ≈ STATIC) y update_seq bajo (si sale inflado, NO se usó el path de revs frescas → investigar).
    • reconcileDayDb diff=0 sostenido por varias semanas; archivado completo a PG.
    • Bootstrap a database=core; órdenes de ayer cobrables (overlay); cero reset destructivo (vigilar New Relic / Bugsnag). Sin regresiones en tenants no-canario.
  4. (Opcional) DROP físico — tras recon verde sostenido:

    Ventana de terminal
    PATCH /admin/rolling-db/flags/<companyId> body: { "rollingDbDrop": true }

    Prereqs: recon verde por varias semanas, CONFLICTS_ENABLED=true, backup verificado, B7/B8. El rolling-db-drop-eval (4 AM o trigger manual) dropea las elegibles. Verificar _replicator limpio, Órdenes/Ventas completas desde PG, /cluster ya no lista la day-DB.

Migración del catálogo tables-prod → CORE

Sección titulada «Migración del catálogo tables-prod → CORE»

Idéntica a la de staging: warm bajo demanda (revs frescas, sin arrastrar el update_seq) + cutover por invalidación (client_invalidations → re-bootstrap fresh contra CORE con el switch del SDK). La cadena la dispara el cron rotate cuando el tenant tiene rollingDb=true.

Cautelas específicas de producción:

  • Pico de carga en el cutover: la invalidación fuerza a TODOS los devices del tenant a re-descargar el catálogo desde CORE → ventana de bajo tráfico + monitoreo. Considerar por lotes si el tenant tiene muchos devices.
  • SDK viejo: un device sin el switch (≥1.4.17) re-bootstrapearía a tables-prod y se quedaría sin las ediciones de catálogo en CORE. No encender rollingDb hasta que el SDK nuevo esté desplegado y adoptado (web redeploy + app OTA/store). Ver versiones en /tenants (telemetría sdkVersion).
  • Sin sync de vuelta: tras el cutover el catálogo se edita en CORE; tables-prod queda legacy. Rollback (rollingDb=false + invalidar) pierde el delta de catálogo en CORE → preferir rollback temprano.
  • rollingDbDrop=false → deja de dropear (lo dropeado vive en PG + S3).
  • rollingDb=false + invalidar el tenant (POST /admin/invalidate-clients/:id) → device vuelve a tables-prod y re-bootstrapea; invalidate_daily vuelve. El delta de catálogo escrito en CORE durante la ventana NO está en tables-prod → preferir rollback temprano.
  • Deploy: revertir el merge en mainrelease-prod.yml re-despliega la imagen previa.
  • El único paso no trivialmente reversible es el DROP físico — mitigado por TTL 30d + backup diario S3 (~30 snapshots por day-DB) + recon verde como precondición.

Diferencias de cautela respecto a staging (resumen)

Sección titulada «Diferencias de cautela respecto a staging (resumen)»
  1. Migraciones: nunca db:migrate completo a ciegas — bookkeeping de prod vacío.
  2. max_dbs_open: aplicarlo en el CouchDB de prod (no solo el de staging).
  3. SDK: publicado (master) + adoptado en web/app antes de encender rollingDb.
  4. Canario: tenant real chico, con aviso; observación más larga; cutover en ventana de bajo tráfico (pico de re-bootstrap).
  5. Monitoreo: New Relic / Bugsnag en cada paso; rollback inmediato ante reset destructivo o pérdida de órdenes/pagos.