Módulo 2: Anatomy Of An Iceberg Table
Presentación del módulo: la anatomía de una tabla Iceberg
Por qué existe este módulo
El módulo 1 terminó con una promesa cumplida a medias, a propósito. Instalaste PyIceberg, creaste el catálogo kiosko, y cargaste las cuarenta filas de la semana de Kiosko dentro de kiosko.fact_orders con table.append(). Confirmaste, con table.current_snapshot() dejando de ser None, que se creó un snapshot real. Y viste, en el diagrama de la lección 6 de ese módulo, una lista de archivos que aparecieron en disco después de esa carga: un archivo de datos, un manifest file, un manifest list, y dos archivos de metadata. Pero ese módulo nunca abrió ninguno de esos archivos. Nombró la cadena —catálogo → metadata → manifest list → manifest files → archivos de datos— sin mostrar, todavía, qué hay adentro de cada eslabón, ni por qué el orden importa.
Este módulo abre esa caja. No agrega ninguna fila nueva a kiosko.fact_orders —la tabla que vas a inspeccionar es exactamente la misma que dejó el módulo 1, con su único snapshot—; en vez de eso, te lleva a recorrer, uno por uno, los cinco eslabones de esa cadena: primero desde el catálogo, el SqlCatalog respaldado por SQLite que instalaste en la lección 4 de M1; después el archivo de metadata, JSON legible, que declara el esquema y la lista de snapshots; después el manifest list y los manifest files, que no son JSON —son Avro, un formato binario que un editor de texto no puede mostrarte sin ayuda—; y finalmente los archivos de datos, que resultan ser exactamente el mismo Parquet que ya conoces de las seis guías anteriores del ecosistema. Al final de este módulo vas a poder responder, con evidencia de tu propio disco, la pregunta que este módulo existe para contestar: ¿qué apunta a qué, y por qué nada de eso se sobrescribe nunca?
El caso que nos acompaña: la misma tabla, sin una fila nueva
Este módulo no agrega datos a Kiosko. kiosko.fact_orders sigue teniendo exactamente cuarenta filas, el mismo revenue total de 106.15 (S01=38.3, S02=38.8, S03=29.05) que verificó la lección 7 del módulo 1, y —esto es importante— un único snapshot, el que creó table.append() en la lección 6 de ese módulo. Cada script de este módulo parte de ese mismo estado: el catálogo kiosko cargado, la tabla kiosko.fact_orders con sus cuarenta filas, ni una escritura más desde entonces. Vas a ver ese mismo snapshot aparecer, una y otra vez, desde ángulos distintos —como una fila en una tabla SQLite, como un bloque dentro de un archivo JSON, como una fila en un pyarrow.Table que devuelve table.inspect.snapshots()— y en cada ángulo vas a reconocer los mismos números.
Una analogía: el expediente de un caso judicial
Piensa en cómo se organiza el expediente de un caso judicial real. No es un solo documento — es una cadena de referencias, cada una apuntando a la siguiente, y ninguna reemplaza nunca a la anterior:
- La carátula del expediente es lo primero que cualquiera consulta: dice el número de caso, la fecha de la audiencia más reciente, y —lo más importante— a qué índice de pruebas hay que ir para ver la evidencia vigente de esa audiencia. La carátula no contiene ninguna prueba en sí misma.
- El índice de pruebas de esta audiencia es una lista: "prueba 1, en la carpeta A; prueba 2, en la carpeta B; prueba 3, en la carpeta C". El índice no contiene las pruebas — apunta a dónde están.
- Cada carpeta de evidencia contiene un lote de pruebas relacionadas, con su propio sub-índice: "esta carpeta tiene 3 fotos, agregadas en esta audiencia; 0 fotos heredadas de audiencias anteriores; 0 fotos retiradas".
- Y finalmente, dentro de cada carpeta, están las fotos concretas — la evidencia real, tal cual se ve, sin ninguna capa de indirección más.
Si en la siguiente audiencia se agrega una prueba nueva, nadie reescribe la carátula vieja, ni el índice viejo, ni las carpetas viejas — se archiva una carátula nueva, que apunta a un índice nuevo, que puede reutilizar las carpetas viejas (si esas pruebas siguen siendo válidas) y agregar carpetas nuevas para lo que cambió. El expediente completo, con todas sus versiones anteriores, sigue existiendo, disponible para quien necesite reconstruir "¿cómo se veía este caso en la audiencia 2?".
Esta guía usa exactamente esa cadena para Apache Iceberg: la carátula es el archivo de metadata (lección 3); el índice de pruebas de esta audiencia es el manifest list (lección 4); las carpetas de evidencia son los manifest files (también lección 4); y las fotos concretas son los archivos de datos — Parquet, el mismo de siempre (lección 5). El catálogo (lección 2) es, en esta analogía, el mostrador de la corte que te dice, sin que tengas que buscar entre cien carátulas viejas, "el expediente vigente de este caso está en esta carátula exacta, ahora mismo".
Diagrama: la cadena completa, de un vistazo
flowchart TB
CAT["Catalogo: kiosko\n(kiosko_catalog.db, SQLite)\n<< el mostrador de la corte >>"]
META["Archivo de metadata\n(metadata/*.metadata.json)\n<< la caratula del expediente >>"]
MLIST["Manifest list\n(metadata/snap-*.avro)\n<< el indice de pruebas de esta audiencia >>"]
MFILE["Manifest file(s)\n(metadata/*-m0.avro)\n<< las carpetas de evidencia >>"]
DATA["Archivo(s) de datos\n(data/*.parquet)\n<< las fotos concretas >>"]
CAT -->|"metadata_location apunta a"| META
META -->|"snapshot vigente.manifest-list apunta a"| MLIST
MLIST -->|"lista"| MFILE
MFILE -->|"lista"| DATA
Fíjate en la dirección de las flechas: cada eslabón apunta hacia adelante, nunca contiene directamente lo que el siguiente eslabón contiene. El catálogo no sabe qué esquema tiene la tabla —eso vive en el metadata—; el metadata no sabe qué archivos Parquet existen —eso vive, indirectamente, detrás del manifest list y los manifest files—. Cada capa resuelve un problema distinto, y esa separación es exactamente lo que hace posible que agregar un archivo de datos nuevo (una escritura futura) no obligue a reescribir ningún archivo de datos viejo.
El mapa de este módulo
Leccion Que eslabon de la cadena cubre
──────── ──────────────────────────────────────────────────────────────
L1 (esta) El mapa completo de la cadena, antes de abrir nada
L2 El catalogo: kiosko_catalog.db como un puntero al metadata vigente
L3 El archivo de metadata: JSON legible, esquema, particion, snapshots
L4 El manifest list y los manifest files: Avro, no JSON
L5 Los archivos de datos: el mismo Parquet que ya conoces
L6 Recorrido completo en disco, con comandos de shell reales
L7 La misma cadena, ahora con la API de inspeccion de PyIceberg
L8 Proyecto: la anatomia completa de kiosko.fact_orders, mapeada
Las lecciones 2 a 5 recorren la cadena de arriba hacia abajo, un eslabón por lección, cada una con código real ejecutado sobre kiosko.fact_orders. La lección 6 repite el recorrido completo, pero esta vez desde la terminal, con ls, file y cat, para que veas con tus propios ojos la diferencia entre un archivo legible y uno que no lo es. La lección 7 muestra que PyIceberg ya empaqueta esa misma inspección en cuatro métodos de una sola línea —table.inspect.snapshots(), table.inspect.manifests(), table.inspect.files(), table.history()—, que vas a usar sin pausa durante el resto de esta guía. Y la lección 8 junta las siete anteriores en un solo script que mapea la anatomía completa de la tabla, de punta a punta.
La frontera: qué NO entra en este módulo
Este módulo inspecciona un catálogo 100% local: kiosko_catalog.db, respaldado por SQLite, exactamente el mismo que instalaste en la lección 4 del módulo 1. Los catálogos de producción —REST, AWS Glue Catalog, Unity Catalog, Polaris— resuelven el mismo problema de fondo (un único puntero al metadata vigente, con control de concurrencia) sobre infraestructura gestionada en la nube, con autenticación y permisos reales; esta guía los nombra recién en el módulo 7, sin implementarlos. Aquí, "catálogo" significa, sin ambigüedad, una fila en una base de datos SQLite en tu propio filesystem.
Tampoco entra en este módulo ninguna escritura nueva. kiosko.fact_orders termina este módulo exactamente con la misma cuarenta filas y el mismo único snapshot con el que empezó — crear un segundo snapshot, y viajar entre ambos, es con precisión el trabajo del módulo 3.
Errores comunes
Pensar que "inspeccionar la anatomía" requiere modificar la tabla. Qué pasa: alguien, al llegar a este módulo, espera que las lecciones le pidan cargar más datos o crear una tabla nueva. Por qué pasa: los módulos anteriores del ecosistema —y el propio módulo 1 de esta guía— casi siempre terminaban con una escritura nueva. Cómo detectarlo: si terminas este módulo y table.inspect.snapshots() muestra más de una fila, revisa qué script corriste — ningún ejemplo trabajado de este módulo llama a table.append(), table.overwrite() ni ninguna otra operación de escritura. Cómo corregirlo: este módulo es de solo lectura sobre el estado que dejó el módulo 1; si accidentalmente escribiste algo nuevo, puedes seguir usando la tabla igual —solo vas a ver más de un snapshot en las lecciones posteriores de lo que ellas mismas predicen—.
Intentar abrir un archivo .avro con un editor de texto y concluir que está "roto" o "corrupto". Qué pasa: alguien, con la curiosidad sana de mirar todo, abre un manifest file o un manifest list con cat, vim, o el editor de texto de su sistema operativo, ve una mezcla de texto legible y símbolos ilegibles, y asume que algo se dañó. Por qué pasa: el archivo de metadata sí es JSON legible, así que es razonable esperar que los demás archivos de la carpeta metadata/ también lo sean. Cómo detectarlo: si ves fragmentos de texto reconocible (como el JSON del esquema) mezclados con caracteres sin sentido, eso es exactamente lo esperado de un archivo Avro con compresión — no es corrupción, es el formato correcto de PyIceberg. Cómo corregirlo: la lección 4 y la lección 6 de este módulo muestran, con evidencia real, por qué esto pasa y cómo inspeccionar esos archivos correctamente —con table.inspect.manifests()/table.inspect.files() de PyIceberg, nunca abriéndolos como texto plano—.
Ejercicios
Ejercicio 1 — Reconstruye la analogía completa, sin mirar atrás. Sin volver a leer la sección de la analogía de esta lección, escribe de memoria los cinco eslabones de la cadena judicial (carátula, índice de pruebas, carpetas de evidencia, fotos concretas, mostrador de la corte) y a qué pieza real de Iceberg corresponde cada uno.
Ver solución
Mostrador de la corte → catálogo (kiosko_catalog.db); carátula del expediente → archivo de metadata (*.metadata.json); índice de pruebas de esta audiencia → manifest list (snap-*.avro); carpetas de evidencia → manifest file(s) (*-m0.avro); fotos concretas → archivo(s) de datos (*.parquet). El orden importa: el mostrador te lleva a la carátula vigente, la carátula te lleva al índice de esta audiencia, el índice te lleva a las carpetas, y las carpetas te llevan a las fotos — nunca al revés, y nunca hay un salto que se salte un eslabón.
Ejercicio 2 — Predicción: ¿cuántos archivos esperas encontrar en cada categoría? Basándote en lo que aprendiste en el módulo 1 —una tabla creada vacía (lección 5), y después una sola carga con table.append() (lección 6)— predice cuántos archivos de metadata JSON, cuántos manifest lists, cuántos manifest files, y cuántos archivos de datos deberías encontrar en kiosko_warehouse/kiosko/fact_orders/ antes de correr ninguna lección de este módulo.
Ver solución
Dos archivos de metadata JSON (uno de la tabla vacía en la lección 5 de M1, otro del primer snapshot en la lección 6), un solo manifest list (el snapshot único que existe), un solo manifest file (ese snapshot agregó un solo lote de datos), y un solo archivo de datos (las cuarenta filas cupieron en un único Parquet). La lección 6 de este módulo confirma esta predicción contando los archivos reales en disco.
Ejercicio 3 — Explica en tus propias palabras por qué "nada se sobrescribe" no es solo un eslogan. En 2-3 frases, y sin mirar el módulo 1 todavía, explica qué evidencia concreta —no una promesa de marketing— vas a poder mostrar al final de este módulo para probar que Iceberg nunca sobrescribe un archivo existente.
Ver solución
No hay una única respuesta correcta —es una hipótesis para verificar—, pero la evidencia concreta que este módulo va a mostrar es contar archivos: vas a ver, en disco, dos archivos .metadata.json distintos (uno de la tabla vacía, otro de la primera carga) coexistiendo en la misma carpeta, ninguno borrado. Esa coexistencia —no una afirmación en un texto, sino un ls real mostrando ambos archivos con marcas de tiempo distintas— es la prueba de que "nada se sobrescribe" es un comportamiento verificable, no solo una promesa.
Resumen y siguiente paso
En esta lección recorriste, sin ejecutar todavía ningún código de inspección, el mapa completo de la cadena que este módulo va a abrir: catálogo → metadata → manifest list → manifest files → archivos de datos, con la analogía del expediente judicial como guía. Confirmaste que este módulo no agrega ninguna fila nueva a kiosko.fact_orders — solo abre lo que el módulo 1 ya dejó escrito.
Antes de avanzar deberías poder: nombrar los cinco eslabones de la cadena, en orden; y explicar por qué cada eslabón apunta hacia adelante en vez de contener directamente lo que el siguiente contiene.
La lección 2 abre el primer eslabón: el catálogo, la pieza más simple de las cinco, y la que hace posible que cualquier lector encuentre el metadata vigente sin tener que adivinar.
Recursos
- Apache Iceberg — documentación oficial, "Table Spec", la definición formal de la cadena metadata → manifest list → manifest file → data file que este módulo recorre. iceberg.apache.org/spec. En inglés.
- PyIceberg — referencia de API, los métodos
table.inspect.*que la lección 7 de este módulo usa a fondo. py.iceberg.apache.org/api. En inglés. - DISEÑO de esta guía — la sección "Anatomía de la tabla" (M2), fuente de la lista exacta de comandos que este módulo ejecuta.
src/guides/lakehouse-and-iceberg-guide/DISENO.md. En español.