Fase 4 — Hallazgos + health del stack (cierre de sesión)
Ejecutado el 2026-07-03. Approach: fase-4-approach.md
Resumen: el eslabón nuevo de Fase 4 (aterrizar un Parquet de R2 como dataset Iceberg gobernado y legible) está verificado en vivo contra R2 + Lakekeeper + Supabase reales. El auto-chain completo (transfer EDC → landing) queda con un edge por rematar (el transfer R2→R2) documentado abajo.⚠️ HISTÓRICO (spike Fase 4, 2026-07-03). El runtime real hoy =
rest→Lakekeeper (ver ../INFRA.md). El ml-runner "slim" (main_lakehouse.py/Dockerfile.lakehouse) y el spike SQLite (iceberg_spike.py) fueron retirados; las creds EDC ahora salen de.env.local. Este doc queda solo como registro histórico.
1. Health del stack (2026-07-03)
| Servicio | Estado | Nota |
|---|---|---|
ml-runner (slim ml-runner-lh, :8000) | ✅ healthy | /health ok; endpoints /lakehouse/* sirviendo. Imagen slim (sin AutoGluon) — ver §4. |
EDC provider/consumer (:19193/:29193) | ✅ | Mgmt API responde; auth ok (sin key → 401, con key → pasa). |
MinIO (:9000) | ✅ | health 200. |
| Redis | ✅ | alcanzable con .env.local (worker in-process E2E ok). |
| Supabase | ✅ | remoto; escrituras datasets/project_files/dataspace_transfers ok. |
workers container (storelyai-workers) | ❌ crash-loop | REDIS_URL no seteada en el contenedor — pre-existente, NO es código de dataspaces (peta al importar lib/queues/definitions.ts, antes de cualquier lógica). Fix: pasar REDIS_URL en el env del servicio workers (docker-compose). |
| prometheus / grafana | ✅ | monitoring. |
2. E2E — resultados
✅ Eslabón NUEVO de Fase 4 (landing) — verificado de cabo a rabo
scripts/dataspaces/edc-landing-e2e.ts contra R2 + ml-runner + Supabase reales:
transfer(COMPLETED, dataDestination=Parquet en R2)
→ landConsumedTransfer:
inspect-parquet (schema BASE) → id/name/score
createDatasetArtifact → dataset "EDC: …" (datasets + project_files)
writeIcebergNativeFromParquet → snapshot nativo (ledger + sync_log + stats)
→ datasets.row_count = 3, status active, service edc_consumer
→ read-back (ml-runner /lakehouse/read): HTTP 200, 3 filas REALES con identidad:
{__row_index:0, __row_id:…, id:1, name:"alice", score:1.5}
"Aparece un dataset legible en el workspace" = demostrado. El Parquet del partner se vuelve un dataset de primera clase (con __row_index/__row_id), leído por el read-router como cualquier dataset Carbon.
También probado aislado (turno previo): inspect-parquet + ingest-parquet directos → tabla Iceberg nativa (rows:3, snapshot).
🟡 Auto-chain completo (transfer EDC → landing) — 1 edge pendiente
scripts/dataspaces/edc-full-e2e.ts: publish → catalog → negotiate → transfer → worker → land.
- Negociación ✅ (NEGOTIATING → el worker inicia el transfer).
- Transfer se quedó en TRANSFERRING (no COMPLETED). Causa probable: es la primera vez que el provider LEE de R2 (en Fase 2 el provider leía de MinIO y escribía en R2; el sentido inverso no se había probado). No es el landing (que ya funciona) — es el data plane S3 del conector leyendo R2.
- Los otros eslabones del chain ya estaban probados: DSP cycle + transfer→R2 (Fase 2 money-shot), executor + worker BullMQ (Fase 3).
→ Edge a rematar en la próxima sesión: hacer que el transfer EDC con source en R2 (o MinIO→R2 con un Parquet real en MinIO) llegue a COMPLETED, para encadenar automáticamente con el landing ya verificado.
3. Known issues / pendientes
| Tema | Detalle |
|---|---|
workers container crash-loop | Falta REDIS_URL en su env (config del contenedor, no código). |
| Transfer EDC R2→R2 | Provider leyendo de R2 no completa (§2). El landing es independiente y funciona. |
consume no autofija el destino R2 | El dataDestination se pasa como parámetro; falta que consume lo defaultee al inbox R2 con keyName plano único. |
| Cleanup de catálogo desde contenedor efímero | drop_table desde un docker run aparte devolvió NoSuchTable aunque la lectura en vivo funcionó; los ficheros R2 sí se borraron. Matiz de config de catálogo entre procesos — footnote, no bloqueante. |
4. Build de ml-runner — nota operativa
- El
requirements.txtde prod tarda ~1h en el primer build (AutoGluon tabular+timeseries + stack ML, varios GB). Es de una sola vez:COPY appva tras la capapip install→ cambios.pyreconstruyen en segundos (caché de capa). - Para iterar en el camino lakehouse/dataspaces se creó un ml-runner SLIM:
requirements-lakehouse.txt+app/main_lakehouse.py(monta solo el router de lakehouse) +Dockerfile.lakehouse. Compila en ~30 s.app.main:appimporta todos los routers (train→AutoGluon), por eso el entrypoint slim.
5. Definition of Done (Fase 4)
-
/lakehouse/inspect-parquet+/lakehouse/ingest-parquet(reutilizanrun_lakehouse_ingest). -
writeIcebergNativeFromParquet(3er hermano del writer nativo) +lakehouse-landing.ts+ hook del worker. - Landing E2E: Parquet R2 → dataset gobernado (
datasets+project_files) + legible (read-router, con identidad). - Cero cambios al control plane / ledger / oracle: reúso puro.
- Auto-chain: transfer EDC (source R2) → COMPLETED → land (edge §2).
-
consumeautofija eldataDestinationR2.
6. Cómo levantar/parar (para retomar)
# ml-runner slim (lakehouse)
docker build -f services/ml-runner/Dockerfile.lakehouse -t ml-runner-lakehouse services/ml-runner
docker run -d --name ml-runner-lh -p 8000:8000 --env-file services/ml-runner/.env.lakehouse -e ML_RUNNER_TOKEN=<token> ml-runner-lakehouse
# EDC connectors + MinIO
docker compose -f services/edc-service/docker-compose.yml up -d
# smokes (dotenv + tsx)
npx dotenv -e .env.local -e services/ml-runner/.env.lakehouse -- tsx scripts/dataspaces/edc-landing-e2e.ts # (con E2E_SRC_KEY)
# parar
docker rm -f ml-runner-lh; docker compose -f services/edc-service/docker-compose.yml down -v