Published

Runbook — Control-plane DB migration drift

Connect any source, model it as an ontology, transform it, and operationalize it, analytics, automation and machine learning, under one governed, self-hostable roof. --- Most teams stitch the...

Runbook — Control-plane DB migration drift

Fecha de la auditoría: 2026-07-09 BD auditada: la Postgres de control-plane detrás de .env.local (SUPABASE_DB_URL) — la que golpean canaries y workers. Herramienta: scripts/dataspaces/audit-migration-drift.ts (npx dotenv -e .env.local -- tsx scripts/dataspaces/audit-migration-drift.ts)

Por qué existe este runbook

Durante el trabajo de 2d.3 (write nativo) se detectó que record_dataset_schema_version fallaba en cada write nativo con column reference "version_number" is ambiguous, dejando el versionado de esquema silenciosamente sin emitir filas (recordSchemaVersion es best-effort, no hace rollback). El fix ya estaba en el repo (20260634) pero no estaba aplicado en la BD.

La auditoría posterior mostró que no es un caso aislado: esta BD no tiene tracker de migraciones (supabase_migrations.schema_migrations no existe), las migraciones se aplican manual/selectivamente, y muchas fueron saltadas — repartidas por todo el rango, no un corte limpio. Lección clave: un fix CREATE OR REPLACE dentro de una migración no-aplicada queda invisible hasta que algo lo re-afirma; y re-correr una migración vieja puede pisar un fix posterior.

Estado tras la auditoría (231 migraciones en repo)

✅ Reconciliado en esta pasada (bugs de columna ambigua — mismo patrón de shadowing)

Aplicado a la BD viva (idempotente, wire-compatible):

MigraciónObjetoAntes (vivo)Ahora
20261251 (re-asserts 20260634 fn#3)record_dataset_schema_versionversion_number sin calificardsv.version_number
20260634 fn#2rebind_object_type_propertyobject_type_id sin calificarotd.object_type_id
20260634 fn#1rebind_link_type_foreign_keydataset_id sin calificarot.dataset_id

Los tres tenían el mismo bug latente: una columna referenciada sin alias que colisiona con un OUT param de RETURNS TABLEvariable_conflict=error (PG14+) revienta el RPC en runtime. Las dos rebind_* eran latentes (sólo disparan si se invoca el rebind de ontología); ya no.

⏸️ CERRADO como DIFERIDO (migraciones de FEATURE, no bugfixes) — revisado 2026-07-09

Decisión (dueño): las suites de feature están ausentes a propósito en esta BD; sólo reconciliamos bugfixes. Estas dos NO son bugfixes → se difieren, deben aterrizar CON su contraparte de código de app, no sueltas:

  • 20261228_objects_base_properties — feature "decouple ontology from dataset_rows" (Stage 1 · Fase 3 · Track A). Añade objects.base_properties (+backfill) y reescribe objects_resolved + fn_instantiate_links_* para leer de base_properties en vez de dr.data. Acoplada al código de app: los sync writers (objects.sync + handleBulkCreate) deben poblar base_properties desde Iceberg. Aplicar sólo la mitad-BD haría que los objetos nuevos creados por el código actual (que aún lee dr.data) caigan al fallback o.properties → divergencia. Aterriza CON el trabajo de decoupling de ontología (parte del lakehouse activo), no ahora. Estado vivo: columna ausente, funciones leen dr.data (consistente).

  • 20260626_property_resolution_events — feature "BindingTimeline" (Phase C): log append-only property_resolution_events + RPCs rebind/acknowledge que insertan eventos. Estado vivo: la tabla existe (0 filas) pero los RPCs NO loguean eventos (mitad aplicada). Suite de ontología dormida → diferido. OJO — conflicto de linaje (bugfix latente, NO aplicar 20260626 tal cual): el rebind_object_type_property de 20260626 está SIN aliasear (tiene el bug de columna ambigua) y el de 20260634 (ya aplicado, aliaseado) DROPPEÓ el INSERT de eventos. O sea el cuerpo canónico del repo (20260634, el último) perdió el logging de 'rebound' que 20260626 agregaba. Si algún día se activa BindingTimeline aquí, rebind necesita un cuerpo FUSIONADO = aliaseado (20260634) + el INSERT de eventos (20260626); ninguna migración lo tiene hoy. Aplicar 20260626 crudo re-introduciría el ambiguous-column que acabamos de cerrar. (Verificado: aplicar 20260634 NO regresó el logging — el vivo estaba en el base 20260625 sin INSERT, 0 filas.)

⏸️ Suites de FEATURE ausentes por completo (dormidas en este entorno)

Tablas + funciones nunca aplicadas. Probablemente porque esas features no se ejercen en esta BD. Aplicar sólo cuando/si la feature corra aquí (varias crean tablas + policies + grants):

  • ML (2026080020260807): tablas ml_models, ml_experiments, ml_runs, ml_model_training_data, ml_evaluations; RPCs ml_start_runml_record_evaluation, ml_transition_stage, healthz + reapers. (El ml-runner es un servicio Railway aparte; el plano de control ML no vive en esta BD.)
  • SDK ontología (20260701/20260702): sdk_upsert_object, sdk_upsert_objects_batch, sdk_archive_object, sdk_create_link, sdk_delete_link, healthz_ontology_invariants.
  • Objetos RPCs (20260610/20260611): fn_object_merge_properties_by_id/_by_pk, fn_delete_objects_with_links.
  • Sueltas: graph_saved_views (+trigger) 20260501; page_shares 20260616; notebook_cell_snapshots (+latest_notebook_cell_snapshot) 20260710; cleanup_expired_idempotency_keys 20260609; reopen_pull_request 20260658; warehouse_project_to_schema 20261232.
  • Columnas sueltas: action_type_executions.execution_kind 20260612; object_links.source_idempotency_key 20260700.

Cómo re-auditar / re-reconciliar

  1. npx dotenv -e .env.local -- tsx scripts/dataspaces/audit-migration-drift.ts
  2. Secciones: (1) tablas ausentes, (2) funciones ausentes, (3) funciones con cuerpo driftado (el detector de reverts — muestra la primera divergencia normalizada), (4) columnas ausentes.
  3. Para un bug de columna ambigua: aplicar la migración que lo arregla con workerDb.query(readFileSync(<migration>)) (protocolo simple corre multi-statement).
  4. Verificar con el canary correspondiente (p.ej. npm run canary:pipeline-output, que ahora asserta que el output recibe su fila v1 en dataset_schema_versions).

Riesgo abierto

Sin tracker de migraciones, el drift es la norma, no la excepción. Considerar: (a) adoptar un tracker (schema_migrations) para que "aplicado" sea verificable, o (b) correr este audit en CI contra la BD de cada entorno. Hasta entonces, correr el audit antes de confiar en cualquier RPC recién arreglado.