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.
Diferencias clave staging → producción
Sección titulada «Diferencias clave staging → producción»| Aspecto | Staging | Producción |
|---|---|---|
| Branch socket | develop | main |
| Branch sdk (release) | tag sobre master | tag sobre master (igual) |
| Workflow socket | release-staging.yml (Env staging) | release-prod.yml (Env production) |
| ArgoCD ns | tables-socket-staging | tables-socket |
| Migraciones | Job in-cluster | Job migrate de release-prod.yml o Job in-cluster |
DB_PREFIX | staging | el actual de prod (no cambiar) |
| CouchDB | cluster staging | cluster prod (max_dbs_open acá) |
| Canario | tenant de prueba | tenant real chico, con aviso |
Pre-requisitos de producción (bloqueantes)
Sección titulada «Pre-requisitos de producción (bloqueantes)»- Staging validado (criterio de salida del runbook de staging).
max_dbs_openaplicado en el CouchDB de PROD (bites-infrastructure/kubernetes/couchdb/values.yaml→couchdbConfig.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 != falseen prod (confirmar).- Ventana de cambio acordada + canal con operaciones. Plan de rollback leído.
Mecánica de deploy (producción)
Sección titulada «Mecánica de deploy (producción)»- Branch model:
main(socket) = prod. Push amain→release-prod.yml: jobstest → migrate → build-and-push(este último gateado por el Environmentproduction) → escribe el tag enk8s/overlays/prod/kustomization.yamlcon[skip ci]→ ArgoCD sincronizatables-socket(prod). - Cluster: contexto kubectl
ticketplus/do-production-bites, namespacetables-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.
Migraciones en producción (paso crítico)
Sección titulada «Migraciones en producción (paso crítico)»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:
- Verificar qué hay (
select * from drizzle.__drizzle_migrations order by created_at;). - Si está vacío: sembrar las entradas
0000–0011como ya aplicadas (sin re-ejecutar SQL), de modo quedb:migratesolo aplique0012 → 0015. - Recién entonces correr
db:migrate(Job in-cluster conGIT_REF: main, o el jobmigratederelease-prod.ymlconMIGRATION_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)»-
Pre-merge: pre-requisitos cumplidos. Migración de prod planificada.
-
Publicar el SDK (master) + promover web/app: bump + tag del SDK sobre
master→ publish (Envproduction). web/app a sus ramas de prod consumiendo la versión publicada. Validar: con flags off, device CORE-only (paridad). -
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. -
Promover socket
develop→main: dispararelease-prod.yml. El jobmigratedebe ser no-op si ya migraste (idempotente).build-and-pushqueda pendiente del Environmentproduction→ aprobar tras confirmar la migración. Validar (todo OFF):GET /admin/rolling-db/{cluster,tenants}consys_admin→ 200; sin rol → 403.GET /admin/crons→ 200.- Logs prod:
day-db-cronagendado,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.
-
Deploy consola backoffice (prod) (opcional, para observar): validar
/cluster+/tenantsreales contra prod.
Activación por-tenant en producción (canario real)
Sección titulada «Activación por-tenant en producción (canario real)»-
Encender
rollingDben el canario:Ventana de terminal PATCH /admin/rolling-db/flags/<companyId> body: { "rollingDb": true } -
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-runEsto 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. -
Verificar (observación más larga que en staging):
- day-DBs PREWARMED/ACTIVE;
..._corecon catálogo (doc_count≈ STATIC) yupdate_seqbajo (si sale inflado, NO se usó el path de revs frescas → investigar). reconcileDayDbdiff=0sostenido 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.
- day-DBs PREWARMED/ACTIVE;
-
(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. Elrolling-db-drop-eval(4 AM o trigger manual) dropea las elegibles. Verificar_replicatorlimpio, Órdenes/Ventas completas desde PG,/clusterya 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-prody se quedaría sin las ediciones de catálogo en CORE. No encenderrollingDbhasta que el SDK nuevo esté desplegado y adoptado (web redeploy + app OTA/store). Ver versiones en/tenants(telemetríasdkVersion). - Sin sync de vuelta: tras el cutover el catálogo se edita en CORE;
tables-prodqueda legacy. Rollback (rollingDb=false+ invalidar) pierde el delta de catálogo en CORE → preferir rollback temprano.
Rollback (los flags son reversibles)
Sección titulada «Rollback (los flags son reversibles)»rollingDbDrop=false→ deja de dropear (lo dropeado vive en PG + S3).rollingDb=false+ invalidar el tenant (POST /admin/invalidate-clients/:id) → device vuelve atables-prody re-bootstrapea;invalidate_dailyvuelve. El delta de catálogo escrito en CORE durante la ventana NO está entables-prod→ preferir rollback temprano.- Deploy: revertir el merge en
main→release-prod.ymlre-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)»- Migraciones: nunca
db:migratecompleto a ciegas — bookkeeping de prod vacío. max_dbs_open: aplicarlo en el CouchDB de prod (no solo el de staging).- SDK: publicado (master) + adoptado en web/app antes de encender
rollingDb. - Canario: tenant real chico, con aviso; observación más larga; cutover en ventana de bajo tráfico (pico de re-bootstrap).
- Monitoreo: New Relic / Bugsnag en cada paso; rollback inmediato ante reset destructivo o pérdida de órdenes/pagos.