⭐⭐⭐ C·6·0 · LA MEDIDA DEL TUBO — cuánto queda, y de qué está hecho
Generado, no escrito a mano:
scripts/ingesta/c6-0-medir-el-tubo.ts.
Supersede en alcance al censo de capacidades de C·0, que sigue valiendo para el
qué — aquí está el cuánto y el si alguien lo usa.
1 · La superficie · 59 ficheros · 9 711 líneas
⚠️ CORREGIDO. La primera medida dijo 85 ficheros / 11 839 líneas porque contaba
lib/triggers/polling-*como orquestación del tubo. No lo es: es la cola
trigger-pollingde los workflows del canvas, sin una sola referencia adatasets
ni al writer. Lo cazó el trinquete al dar un falso positivo sobre
canvas-workflows/[id]/activate— 27 ficheros y 2 348 líneas de otra funcionalidad.
⭐ Un censo que se equivoca de frontera cuenta de más, y contar de más para justificar
un borrado es la peor manera de equivocarse.
| subsistema | fich. | líneas |
|---|---|---|
| CDC (log de replicación) — Oracle LogMiner, redo parser, snapshots | 34 | 3 283 |
| Polling workers (uno por origen: pg, mysql, oracle, sqlserver, s3, azure, dynamo, hubspot, excel) | 16 | 5 516 |
| Colas de ingesta (las que el scheduler llenaba) | 1 | 220 |
| Sync workers (Iceberg, EDC) | 2 | 245 |
| Cara HTTP del CDC | 4 | 283 |
| Perfiles y disparadores | 2 | 164 |
2 · ⭐⭐⭐ Los tres hallazgos
① El subsistema CDC NUNCA ha corrido
cdc_syncs 0 filas
cdc_sync_runs 0 filas ⇒ ni una sola corrida registrada
34 ficheros y 3 283 líneas —Oracle LogMiner, un parser de redo logs, snapshots de MySQL y Oracle— que no se han ejecutado ni una vez. No es código poco usado: es código sin usar, y llevaba tiempo pareciendo una capacidad.
⭐ Encaja con lo que C·0 ya había olido por otro camino: las estrategias hash y cdc
estaban declaradas en el tipo y sin implementación que las eligiera. El censo de
capacidades vio el enum vacío; éste ve las 3 283 líneas detrás.
② ⭐⭐⭐ El tubo NO está dormido: se sigue llamando y falla en silencio
| datasets con datos de verdad | 55 · la última: 11-ago-2026 (order_items, 112 650 filas) |
| FANTASMAS (0 filas, sin namespace) | 7 · el último: 16-ago-2026 |
El último fantasma es más reciente que la última escritura real. Hoy mismo entró
categorias con row_count = 0 e iceberg_namespace = null: una fila registrada en
Index para una tabla que nunca se materializó.
⇒ Eso cambia la decisión. Un tubo dormido se puede dejar pudrirse; uno que se invoca y falla sin decirlo produce daño cada vez: ocupa un nombre, deja una fila que nadie reconcilia, y le enseña al usuario un dataset vacío como si la ingesta hubiera funcionado. Es el patrón «el estado dice que está bien», sirviéndose solo.
③ El writer responde 200 — y no significa nada
WAREHOUSE_WRITER_URL (warehouse-writer-production.up.railway.app) contesta 200 en
/health. ⚠️ Ya está apuntado y se vuelve a cumplir: un health-check dice que el
proceso vive; sólo un e2e que ESCRIBA prueba el camino de escritura. El proceso está
sano y el camino roto desde el ~11-ago, cuando dejó de existir el warehouse por inquilino
que le pide a Lakekeeper.
3 · La cobertura, releída contra lo entregado
⚠️ Y esta relectura no es ceremonia: C·0 daba por cubiertas en C·5 la idempotencia y los lotes, y ninguna de las dos estaba cuando C·5 se cerró. Aparecieron al releer, no al escribir. ⇒ Un censo que dice «lo cubre la fase X» hay que releerlo cuando X se cierra, o es una promesa que nadie comprueba.
| capacidad | estado | contra qué se comprobó |
|---|---|---|
Lectura completa (full_scan) | 🏁 | c5-gate-copia · 250 filas + suma en el destino |
| Streaming por lotes (techo de RAM) | 🏁 | fetchsize — sin él pgjdbc materializa todo |
| Idempotencia / REPLACE | 🏁 | dos pasadas, sigue en 250 |
| Esquema y tipos del origen | 🏁 | DESCRIBE sobre la vista JDBC |
| Fan-out por rangos | 🏁 | 4 lectores — era POSPONE en C·0 y hoy está |
| Estrategia automática (ts›seq›full) | ⏭️ | incremental |
Cursor timestamp/sequence | ⏭️ | incremental — §7 dejó el WHERE puesto |
| Modo UPSERT | ⏭️ | incremental — la carga YA es MERGE, sólo cambia el ON |
| Checkpoint / reanudación | ⏭️ | incremental |
| Programación | ⏭️ | el disparador, no el motor: ejecuta la misma sentencia |
| Descubrimiento de FKs | ⏭️ | alimenta UI y grafo; no participa en copiar filas |
Estrategia hash | ⛔ | vocabulario muerto |
Estrategia cdc | ⛔ | idem — y ahora se sabe que son 3 283 líneas sin correr |
Escritura por warehouse-writer | ⛔ | es la causa de que la ingesta esté rota |
5 cubiertas · 6 pospuestas · 3 abandonadas.
⭐⭐ De las 6 pospuestas, CINCO son la misma cosa: cursor, estrategia, upsert, checkpoint y programación son las piezas de un incremental, no cinco huecos sueltos. La sexta (FKs) no participa en copiar filas.
⇒ Suprimir el tubo deja UN agujero con nombre, no seis sin nombre.
4 · Hasta qué punto queda opacado
Lo que el tubo hacía y hoy hace el carril gobernado, mejor:
- copiar de PostgreSQL — ahora es una sentencia, con permisos (
CONNECTION_USE), credencial que no viaja en el SQL, compensación si falla y recuento verificado en el destino; - disparable desde el editor, un notebook o la UI — el mismo camino, porque la UI genera la sentencia;
- y con lo que el tubo no tenía:
SHOW SCHEMAS/TABLES IN <conexión>,CREATE TABLE … LIKE(metadata-only) e idempotencia real.
Lo que el tubo tenía y no está: un incremental — que nunca funcionó (cdc_syncs
a cero), así que no se pierde una capacidad, se hereda una deuda con el alcance ya
medido.
Lo que el tubo tiene y sobra: 9 orígenes distintos con un worker cada uno. El carril
nuevo cubre PostgreSQL. ⚠️ Ésa es la asimetría honesta de este censo y no se tapa:
suprimir el tubo entero retira mysql, oracle, sqlserver, s3, azure, dynamo, hubspot y
excel. La pregunta que decide C·6·1 no es técnica —¿los usa alguien?—, y la contesta
la tabla de §2·②: 55 datasets con datos, todos de postgresql/postgres. Ningún
otro origen ha escrito nunca.
5 · 🏁 EL CORTE (2026-08-16)
El tubo está suprimido, no apagado con un flag — un interruptor deja creer que volver a encenderlo funciona, y no funciona: reactivarlo no ingiere, produce fantasmas.
scripts/start-workers.ts | ya no importa ni arranca los 9 polling workers, el scheduler ni el motor CDC |
| colas del tubo | no se abren, así que no hay ninguna que cerrar |
| trinquete | npm run check:sin-tubo — verde, 3 985 ficheros mirados |
⭐ Lo que el trinquete vigila es la ALCANZABILIDAD, no la existencia. Borrar 9 711 líneas es otra entrega; exigirlo aquí obligaría a desactivar el trinquete hasta hacerlo, y un trinquete desactivado no vigila nada.
⚠️ Y import type no cuenta: se borra al compilar, no arrastra código y no puede
ejecutar una ingesta. Prohibirlo habría obligado a duplicar PartitionPlan y
PartitionOutcome para complacer al vigilante — empeorar el código por el trinquete.
6 · ⏭️ Lo que este censo NO contesta
- Qué se rompe al borrar. Eso lo dice el trinquete de C·6·1: debe fallar si alguien vuelve a llamar al writer desde el camino de ingesta.
- Los 7 fantasmas. Hay que reconciliarlos (
reconcileDropInIndex), y antes hay que cortar quien los produce — reconciliar sin cortar es barrer con el grifo abierto.