Módulo 8: Project Kioskos Lakehouse
El brief: Kiosko necesita un lakehouse, no siete demos
Descripción
La gerencia de Kiosko —la misma gerente de operaciones que en data-engineering-foundations-guide pedía un reporte semanal a mano, y que en data-modeling-for-analytics-guide pidió "un solo warehouse, no siete demos"— tiene ahora una pregunta distinta, dirigida específicamente al equipo que pasó siete módulos aprendiendo Apache Iceberg: "ya sé que cada tabla, por separado, funciona — ¿pero tengo, de verdad, un lakehouse, o tengo siete experimentos con Iceberg que nunca compartieron un catálogo?". Esta lección convierte esa pregunta en un brief concreto, con las mismas exigencias verificables que ya usaron los capstones de data-modeling-for-analytics-guide y dbt-analytics-engineering-guide, adaptadas a lo que Iceberg agrega específicamente: nada de columnas de historia, todo verificable con time travel.
Conexión con el módulo. Esta lección no construye ninguna tabla todavía —eso empieza en la lección 3—. Su trabajo es traducir la pregunta de la gerencia en una lista de exigencias, cada una mapeada a la lección de este módulo que la resuelve, y confirmar, con evidencia citada de los siete módulos anteriores, que el material ya existe.
Una analogía: el certificado de habitabilidad, no un recorrido por obras separadas
Un inspector de construcción que visita, una por una, siete obras distintas de la misma constructora —los cimientos de un edificio, la estructura de otro, el sistema eléctrico de un tercero— puede confirmar, con total honestidad, que las siete están bien hechas. Pero ese inspector no puede firmar un certificado de habitabilidad hasta que las siete piezas sean, físicamente, el mismo edificio: los mismos cimientos sosteniendo la misma estructura, el mismo sistema eléctrico corriendo por las mismas paredes. El certificado de habitabilidad no evalúa la calidad de cada pieza otra vez —eso ya se hizo—; evalúa si, juntas, forman algo que alguien puede habitar. Este módulo es ese certificado: no vuelve a probar que table.overwrite() funciona, o que PartitionSpec evoluciona sin reescribir datos —eso ya lo probaron los módulos 3 y 5—; prueba que las cinco tablas, juntas, en el mismo catálogo, forman un lakehouse habitable.
El brief, en las palabras de la gerencia
"Cada módulo me demostró algo real: que Iceberg recuerda el pasado sin que nadie escriba
valid_from, que puedo agregar una columna sin romper nada, que una tabla de diez millones de filas se puede particionar y evolucionar sin reescribirla entera. Todo eso me convenció. Pero cuando le pregunté a mi equipo 'entonces, ¿ya tengo un lakehouse?', la respuesta fue 'todavía no — cada demo vive en su propio catálogo, creado desde cero, y ninguna sabe que las otras existen'. Necesito una cosa concreta: un solo catálogo, con las cinco tablas de Kiosko conviviendo ahí, y la confirmación de que si le pregunto 'cuánto vendimos, con el margen correcto de la barra energética', la respuesta sigue siendo la misma que ya me dieron seis equipos distintos antes:106.15de revenue,10.8de margen."
Cuatro exigencias concretas salen de ese párrafo, y cada una mapea a una pieza que ya construiste en un módulo anterior:
| Exigencia de la gerencia | Pieza que ya construiste | Módulo |
|---|---|---|
| "un solo catálogo, con las cinco tablas conviviendo" | cada tabla vivía en su propio kiosko_catalog.db, creado desde cero por lección | Módulos 1-7, ensamblado aquí |
"recordar el pasado sin valid_from" | dim_product sin columnas de historia + time travel | Módulo 3 |
| "particionar y evolucionar sin reescribir" | fact_orders_at_scale, PartitionSpec, update_spec() | Módulo 5 |
"el mismo 106.15/10.8 que ya confirmaron seis equipos" | el revenue y margen canónicos de Kiosko, verificados con motores distintos en cada guía | Todo el ecosistema |
La primera fila es la que este módulo agrega de verdad: ningún módulo anterior sirvió las cinco tablas en el mismo catálogo, en la misma corrida. Eso es, exactamente, lo que las lecciones 3 a 6 de este módulo construyen.
Ejemplo trabajado: confirmando qué material tienes antes de ensamblar
Antes de prometerle nada a la gerencia, confirma que las siete piezas de los módulos anteriores están disponibles, con los números exactos que ya conoces, para que el ensamblaje de este módulo no dependa de memoria sino de evidencia.
# confirm_material.py -- ningun catalogo real todavia, solo un inventario verificable
MODULE_DELIVERABLES = [
("Modulo 1", "kiosko.fact_orders cargada", "40 filas, revenue 106.15"),
("Modulo 2", "anatomia de la tabla mapeada", "catalogo -> metadata -> manifest list -> manifest files -> data files"),
("Modulo 3", "dim_product sin historia + time travel", "P002: snacks/10.8 (correcto) vs health-snacks/9.36 (roto)"),
("Modulo 4", "dim_store evolucionada", "country: S01=Colombia, S02=Peru, S03=Chile"),
("Modulo 5", "fact_orders_at_scale particionada", "10,000,000 filas, S01=9,575,000.00, spec evolucionado"),
("Modulo 6", "MERGE INTO + table.upsert()", "rows_updated=1, rows_inserted=0, mismo resultado que overwrite()"),
("Modulo 7", "catalogos + mantenimiento", "expire_snapshots() real, 4 catalogos de produccion nombrados"),
]
print("=== Kiosko: confirmando el material de los 7 modulos, antes de ensamblar el lakehouse ===\n")
for module, deliverable, evidence in MODULE_DELIVERABLES:
print(f"{module:10} {deliverable:38} {evidence}")
print("\nFaltante real: NINGUNA capacidad nueva de Iceberg -- falta ensamblar las 5 tablas")
print("en el mismo catalogo. La unica tabla genuinamente nueva de este modulo: kiosko.dim_date.")
Qué esperar. Al correr python3 confirm_material.py, la salida es exactamente esta:
=== Kiosko: confirmando el material de los 7 modulos, antes de ensamblar el lakehouse ===
Modulo 1 kiosko.fact_orders cargada 40 filas, revenue 106.15
Modulo 2 anatomia de la tabla mapeada catalogo -> metadata -> manifest list -> manifest files -> data files
Modulo 3 dim_product sin historia + time travel P002: snacks/10.8 (correcto) vs health-snacks/9.36 (roto)
Modulo 4 dim_store evolucionada country: S01=Colombia, S02=Peru, S03=Chile
Modulo 5 fact_orders_at_scale particionada 10,000,000 filas, S01=9,575,000.00, spec evolucionado
Modulo 6 MERGE INTO + table.upsert() rows_updated=1, rows_inserted=0, mismo resultado que overwrite()
Modulo 7 catalogos + mantenimiento expire_snapshots() real, 4 catalogos de produccion nombrados
Faltante real: NINGUNA capacidad nueva de Iceberg -- falta ensamblar las 5 tablas
en el mismo catalogo. La unica tabla genuinamente nueva de este modulo: kiosko.dim_date.
Siete filas, siete evidencias numéricas concretas, ninguna inventada de nuevo aquí — cada una es, literalmente, el resultado ya verificado del proyecto de cierre de su propio módulo. Fíjate en el patrón: este brief no pide "construir algo nuevo" en el sentido de una capacidad de Iceberg — pide reunir lo que ya existe, en un solo catálogo, de forma que la pregunta de negocio ("¿cuánto vendió Kiosko, con el margen correcto?") tenga una sola respuesta, no siete respuestas dispersas en siete directorios distintos.
Diagrama: de la pregunta de la gerencia al brief técnico
flowchart TD
A["'Cada demo me convencio\npor separado, pero necesito\nUN lakehouse, no siete catalogos'"] --> B["Brief tecnico (esta leccion)"]
B --> C["Un solo catalogo, 5 tablas\nconviviendo -- L3, L4, L5"]
B --> D["Recordar el pasado sin\nvalid_from -- dim_product,\ntime travel (L4)"]
B --> E["Particionar/evolucionar sin\nreescribir -- at_scale (L5)"]
B --> F["El mismo 106.15/10.8\nque 6 motores ya confirmaron"]
C --> G["Lakehouse ensamblado,\nverificado con assert (L8)"]
D --> G
E --> G
F --> G
Profundización: por qué "un solo catálogo" es la exigencia que ningún módulo anterior podía cumplir
Fíjate en algo deliberado de los siete módulos anteriores: cada uno de ellos, en su lección 4 o 5, ejecutó exactamente el mismo patrón de arranque —warehouse_path = os.path.abspath("kiosko_warehouse"), catalog_db_path = os.path.abspath("kiosko_catalog.db"), load_catalog("kiosko", type="sql", uri=..., warehouse=...)—. Ese patrón, repetido siete veces, en siete directorios de trabajo distintos, es exactamente correcto para el propósito pedagógico de cada módulo: aislar una garantía a la vez, sin que el estado de un módulo interfiera con el siguiente. Pero tiene una consecuencia directa que nadie mencionó hasta ahora: los siete kiosko_catalog.db que generaste, uno por módulo, son siete catálogos físicamente distintos, cada uno con su propia tabla kiosko.fact_orders, su propio kiosko.dim_product, sin ninguna relación entre sí.
Eso es, con precisión, lo que la gerencia detectó en su cita: "cada demo vive en su propio catálogo, creado desde cero". Este módulo resuelve esa fragmentación de la única forma honesta posible — no fusionando los siete catálogos anteriores (una operación que Iceberg no ofrece, y que tampoco tendría sentido pedagógico), sino reconstruyendo las cinco tablas finales, una sola vez, en un catálogo nuevo y único, exactamente como hicieron los capstones de data-modeling-for-analytics-guide y dbt-analytics-engineering-guide antes. La lección 3 abre ese catálogo único; las lecciones 4, 5 y 6 siguen escribiendo sobre él, sin volver a crear uno nuevo por lección — la primera vez en toda esta guía en que varias lecciones consecutivas comparten, a propósito, el mismo kiosko_warehouse/.
Errores comunes
Interpretar el brief como una petición de una sexta tabla o una capacidad de Iceberg nueva. Qué pasa: alguien, al leer "necesito un lakehouse real", asume que el brief pide investigar algo que los siete módulos anteriores no cubrieron —un tipo de transform de partición nuevo, una operación de mantenimiento adicional—. Por qué pasa: "lakehouse real" suena a algo más grande que lo ya construido, y es fácil confundir "más grande" con "más técnicas". Cómo detectarlo: si tu plan para este módulo incluye aprender un método de la API de PyIceberg que no aparece en ningún módulo anterior, te desviaste del brief — relee la tabla de esta lección, las siete filas ya cubren todo lo que el negocio pidió. Cómo corregirlo: el brief pide ensamblaje en un catálogo compartido, no invención — las cuatro exigencias de la gerencia se resuelven reconstruyendo, una sola vez, las tablas que ya construiste, no diseñando una octava técnica.
Asumir que "un solo catálogo" significa fusionar los siete catálogos ya existentes de los módulos anteriores. Qué pasa: alguien busca, en la API de PyIceberg, un método para combinar o importar tablas de un SqlCatalog existente hacia otro. Por qué pasa: "un solo catálogo con las cinco tablas" suena, a primera lectura, a una operación de migración entre catálogos. Cómo detectarlo: si tu búsqueda en la documentación de PyIceberg no encuentra ningún método de "fusión de catálogos" (porque no existe, y no hace falta), estás buscando la solución equivocada. Cómo corregirlo: la lección 3 no fusiona nada — reconstruye las tablas desde los mismos datos fijos de Kiosko (RAW_ORDERS, DIM_STORE_ROWS, etc.) en un catálogo nuevo, exactamente como cada proyecto de cierre de los módulos anteriores ya hizo con su propia tabla individual.
Tratar las cuatro exigencias del brief como independientes entre sí. Qué pasa: alguien construye las cinco tablas en el mismo catálogo, pero nunca las une en una sola consulta que confirme el margen correcto de P002 contra el revenue completo. Por qué pasa: cada exigencia del brief menciona una capacidad distinta (catálogo compartido, time travel, partición, verificación), así que tratarlas como cuatro entregables separados parece natural. Cómo detectarlo: si tu lakehouse final tiene las cinco tablas cargadas pero ninguna consulta que las una —fact_orders + dim_store + dim_date + dim_product AS OF snap_v1—, te falta la pieza que realmente demuestra que es un lakehouse, no solo cinco tablas paralelas. Cómo corregirlo: la lección 4 de este módulo es, precisamente, esa unión — el mismo tipo de JOIN punto-en-el-tiempo conceptual que data-modeling-for-analytics-guide ya enseñó, ahora resuelto con table.scan(snapshot_id=snap_v1) en vez de un BETWEEN valid_from AND valid_to.
Ejercicios
Ejercicio 1 — Explica por qué los siete kiosko_catalog.db de los módulos 1 a 7 no pueden simplemente "copiarse" a un directorio común para formar el lakehouse de este módulo. Piensa en qué pasaría si dos de esos siete catálogos, por casualidad, tuvieran cada uno una tabla llamada kiosko.dim_product con contenido distinto.
Ver solución
Copiar los siete kiosko_catalog.db a un mismo directorio no produciría un lakehouse único — produciría siete archivos SQLite con el mismo nombre, sobrescribiéndose entre sí, o siete catálogos con el mismo namespace kiosko pero contenido en conflicto. El caso concreto que ilustra el problema: el módulo 3 dejó kiosko.dim_product con dos snapshots (V1, V2 vía overwrite()); el módulo 7 reconstruyó una versión distinta de esa misma tabla, con cinco noches adicionales de escrituras redundantes, en su propio catálogo aislado. Si ambos catálogos coexistieran bajo el mismo nombre, no habría ninguna forma determinística de decidir cuál kiosko.dim_product es "la real". La única solución limpia —la que aplica este módulo— es reconstruir las tablas finales, una sola vez, en un catálogo nuevo, a partir de los mismos datos fijos que ya conoces, no intentar fusionar historiales de escritura que nunca fueron pensados para coexistir.
Ejercicio 2 — Encuentra la exigencia implícita sobre reproducibilidad en la cita de la gerencia. Vuelve a leer la cita completa. Sin mirar la tabla de "El brief", identifica una exigencia implícita que no está en la tabla, relacionada con la confiabilidad de los números a lo largo del tiempo.
Ver solución
Una respuesta razonable: la frase "la respuesta sigue siendo la misma que ya me dieron seis equipos distintos antes" implica que el resultado del lakehouse de Iceberg tiene que coincidir, número por número, con guías que usaron motores completamente distintos —DuckDB en data-modeling-for-analytics-guide, dbt+DuckDB en dbt-analytics-engineering-guide, Spark en spark-and-distributed-processing-guide—. Esa exigencia implícita —"el motor de almacenamiento no debería cambiar el resultado de negocio"— es la misma disciplina de reproducibilidad que sostiene toda esta guía: los datos de Kiosko son siempre fijos, nunca generados con random o datetime.now(), precisamente para que comparar 106.15 y 10.8 entre siete guías distintas tenga sentido.
Ejercicio 3 — Argumenta por qué "un solo catálogo con las cinco tablas" es una exigencia distinta de "cinco tablas correctas". En 2-3 frases, explica qué gana Kiosko al tener las cinco tablas en el mismo catálogo que no ganaría con cinco catálogos separados, cada uno con una tabla correcta.
Ver solución
Cinco tablas correctas en cinco catálogos separados demostrarían que cada mecanismo de Iceberg funciona —exactamente lo que ya probaron los módulos 1 a 7—, pero no permitirían responder ninguna pregunta que necesite más de una tabla a la vez: calcular el margen por categoría exige unir dim_product con fact_orders; filtrar por país exige unir dim_store con fact_orders. Un catálogo único, con las cinco tablas conviviendo bajo el mismo namespace kiosko, es lo que convierte cinco piezas correctas en un lakehouse consultable — la diferencia exacta entre tener los ingredientes y tener la receta ya servida en un solo plato.
Resumen y siguiente paso
En esta lección tradujiste la pregunta de la gerencia —"¿ya tengo un lakehouse, o siete demos con Iceberg?"— en un brief con cuatro exigencias concretas, cada una mapeada a una pieza que ya construiste en los siete módulos anteriores. Confirmaste, con confirm_material.py, que las siete piezas están listas, con sus números exactos. Y viste por qué la exigencia central de este módulo —un solo catálogo, con las cinco tablas conviviendo— es algo que ningún módulo anterior, por diseño pedagógico, podía cumplir todavía.
Antes de avanzar deberías poder: repetir, de memoria, las cuatro exigencias del brief y a qué módulo corresponde cada una; y explicar por qué "un solo catálogo" no es lo mismo que "cinco tablas correctas por separado".
La lección 3 empieza a construir: abre el catálogo único de este módulo y carga las tres tablas que no cambian de estado —fact_orders, dim_store con country, y la nueva dim_date—, el primer tercio del lakehouse completo.
Recursos
- DISEÑO de
data-modeling-for-analytics-guide— el mismo patrón de brief ("un solo warehouse, no siete demos") que esta lección adapta a Iceberg.src/guides/data-modeling-for-analytics-guide/DISENO.md. En español. - DISEÑO de
dbt-analytics-engineering-guide— segunda confirmación del mismo patrón de capstone, aplicado a un proyecto dbt completo.src/guides/dbt-analytics-engineering-guide/DISENO.md. En español. - PyIceberg — documentación oficial (quickstart), la base de
load_catalog()que este módulo reutiliza en un catálogo único desde la lección 3. py.iceberg.apache.org. En inglés. - DISEÑO de esta guía — el mapa completo de los ocho módulos, incluida la sección completa de este módulo 8.
src/guides/lakehouse-and-iceberg-guide/DISENO.md. En español.