DEV Community

Cover image for DuckLake 1.0: el formato de data lake que mueve el catálogo de archivos a SQL y promete 926 más velocidad que Iceberg
lu1tr0n
lu1tr0n

Posted on • Originally published at elsolitario.org

DuckLake 1.0: el formato de data lake que mueve el catálogo de archivos a SQL y promete 926 más velocidad que Iceberg

El 13 de abril de 2026, DuckDB Labs liberó la versión 1.0 de DuckLake, un formato de data lake con backward-compatibility garantizada que rompe con la convención de los últimos cinco años en lakehouses: en lugar de almacenar el catálogo de metadatos como una pila de archivos JSON dispersos en object storage —el patrón que usan Apache Iceberg, Delta Lake y Apache Hudi—, DuckLake lo guarda en una base de datos relacional. La consecuencia técnica es directa, y los benchmarks reportados por el propio equipo son contundentes: 926× más rápido en consultas y 105× más rápido en ingesta frente a Iceberg en cargas de trabajo con muchas escrituras pequeñas, según declaraciones de Pedro Holanda, ingeniero principal de DuckDB Labs, recogidas por The Register.

InfoQ cubrió el lanzamiento el 2 de mayo, firmado por Renato Losio. El anuncio oficial en ducklake.select lista las cinco features principales: data inlining, sorted tables, bucket partitioning, soporte completo para tipos GEOMETRY y VARIANT, y deletion vectors compatibles con Iceberg v3. El repositorio público en GitHub ya está disponible bajo licencia open source y los clientes para Apache DataFusion, Apache Spark, Trino y Pandas son operativos al día del release. MotherDuck ofrece una versión hosteada que gestiona la base de datos del catálogo y el storage subyacente.

Este artículo explica, en términos accesibles para ingenieros de datos y devs que están evaluando lakehouses para 2026, qué es DuckLake exactamente, qué problema concreto resuelve, cómo se integra con el stack actual, y cuándo conviene preferirlo sobre Iceberg o Delta Lake. La cobertura es continuación natural de la línea editorial dev del blog —los posts recientes sobre Bun→Rust, pnpm 11 y Cloudflare Agents Week— en una pieza que toca el lado de datos que estaba sub-cubierto.

El problema que ataca: el "small file problem" en lakehouses

Los lakehouses modernos —Iceberg desde Netflix en 2017, Delta Lake desde Databricks en 2019— resolvieron un problema real: dieron transacciones ACID sobre data lakes que antes eran simplemente "carpetas de archivos Parquet sin orden". Para hacerlo, agregaron una capa de metadata que registra qué archivos componen una tabla en cada momento, qué snapshots existen, qué columnas hay, qué particiones aplican. Esa metadata —archivos JSON, Avro o manifiestos específicos del formato— se guarda junto a los datos en object storage como S3, R2 o GCS.

El problema aparece cuando el patrón de uso no es "ingesta batch grande cada hora". Si un workload incluye escrituras pequeñas frecuentes —insertar una fila, actualizar diez registros, borrar una tupla— el resultado es que cada operación genera al menos un archivo Parquet nuevo (Parquet está optimizado para millones de filas, no para una sola) más uno o varios archivos de metadata para registrar el cambio. Hannes Mühleisen, cofundador y CEO de DuckDB Labs, lo describió a The Register con esta cita:

"You make a small change to your table, adding a single row, and it affects data lake performance because... a new file has to be written... and then a bunch of metadata."

El costo se acumula. Tras pocos miles de escrituras pequeñas un usuario termina con decenas de miles de archivos diminutos y consultas que tienen que listar y leer todo ese ruido para responder a una pregunta simple. Las "compaction jobs" que se corren periódicamente para fusionar archivos pequeños en archivos grandes ayudan, pero introducen su propio mantenimiento operacional. Y los object stores cobran por requests: 100.000 archivos pequeños cuestan más que 1.000 archivos grandes con el mismo volumen total de datos.

La solución de DuckLake: catálogo en SQL, datos en Parquet

DuckLake invierte la división. Los datos se siguen guardando como Parquet en object storage —compatibilidad total con el ecosistema existente—, pero el catálogo, los snapshots, los metadatos de schema y la lista de archivos se guardan en una base de datos relacional estándar. Los backends soportados oficialmente al día del release 1.0 son tres: DuckDB (sí, DuckDB puede ser su propio catálogo), PostgreSQL y SQLite.

Esto resuelve el problema de los archivos pequeños de raíz. Cuando llega una escritura de 10 filas, DuckLake no escribe un Parquet nuevo. Acumula esa escritura dentro de la base de datos del catálogo —que está optimizada precisamente para muchas operaciones pequeñas con ACID y transacciones reales—, y solo cuando la acumulación cruza un threshold (default: 10 filas, configurable) descarga las filas a Parquet en object storage. La feature se llama data inlining y es una de las cinco piezas de la 1.0.

-- Crear una tabla DuckLake usando PostgreSQL como catálogo
ATTACH 'postgres://user:pwd@host/dbname' AS catalog (TYPE POSTGRES);
USE catalog;

CREATE TABLE events (
  id BIGINT,
  ts TIMESTAMP,
  user_id VARCHAR,
  payload VARIANT
);

-- Inserts pequeños quedan inlineados en el catálogo
INSERT INTO events VALUES (1, NOW(), 'user-42', '{"action": "click"}');
INSERT INTO events VALUES (2, NOW(), 'user-17', '{"action": "view"}');

-- Cuando la acumulación supera el threshold, descarga a Parquet
-- Sin que el usuario tenga que pensarlo
Enter fullscreen mode Exit fullscreen mode

La diferencia operativa para el usuario es ninguna: el SQL es estándar, los datos viven en S3/R2/GCS como siempre, las consultas se responden con Parquet cuando los datos ya están descargados o desde el catálogo cuando todavía no. La diferencia de performance, en cargas de trabajo con muchas escrituras pequeñas, es del orden de los dos órdenes de magnitud.

Las cinco features de la 1.0 en detalle

Data inlining

El threshold por defecto es 10 filas. Por debajo de eso, las filas viven en la tabla del catálogo. Por encima, se descargan a Parquet. El usuario puede configurar el threshold para subir o bajar la frontera según el patrón de carga: cargas con muchas escrituras pequeñas se benefician de un threshold alto (digamos 1.000), mientras que cargas con muy pocas escrituras pequeñas no necesitan inline en absoluto.

Sorted tables

DuckLake permite declarar el orden lógico de las filas dentro de cada archivo Parquet, usando ya sea nombres de columnas o expresiones SQL arbitrarias:

CREATE TABLE sales (
  region VARCHAR, sale_date DATE, amount DECIMAL
)
SET SORTED BY (region ASC, sale_date DESC);
Enter fullscreen mode Exit fullscreen mode

El motor aprovecha el orden para hacer file pruning y row-group pruning en consultas filtradas. Para una consulta que filtre por region = 'eu' y sale_date > '2026-01-01', el motor lee solo los archivos y row-groups que contienen esas combinaciones, sin escanear el resto.

Bucket partitioning

Particionado basado en hash con murmur3 —el mismo algoritmo que usa Iceberg, lo cual permite migración bidireccional sin reorganizar archivos—. Útil para columnas de alta cardinalidad como user_id o session_id donde el particionado por valor (range) sería ineficiente porque hay millones de valores distintos.

Tipos GEOMETRY y VARIANT

GEOMETRY brinda soporte completo para datos espaciales, sin necesidad de extensiones externas como PostGIS para queries geoespaciales básicas. VARIANT es el tipo equivalente a JSON pero con encoding binario y mejor performance. La diferencia importa cuando se almacenan campos semiestructurados —payloads de eventos, metadata flexible— a escala: VARIANT se serializa más rápido y se filtra más rápido que JSON parseado on-the-fly.

Deletion vectors compatibles con Iceberg v3

DuckLake soporta deletion vectors —el mecanismo donde, en vez de reescribir un archivo Parquet entero al borrar filas, se mantiene un bitmap separado que marca qué filas están eliminadas—. La implementación usa archivos Puffin (formato binario para metadata extensible que Iceberg v3 también usa), lo que mantiene compatibilidad bidireccional con tablas Iceberg.

Comparativa contra Iceberg y Delta Lake

Dimensión
Iceberg / Delta
DuckLake 1.0

Catálogo de metadatos
Archivos JSON / Avro en object storage
RDBMS (DuckDB, Postgres, SQLite)

Manejo de escrituras pequeñas
Crea archivos por operación
Data inlining hasta threshold

Compaction jobs
Operacionalmente requeridos
Automáticos vía descarga periódica

Backend de datos
Parquet en S3/R2/GCS
Parquet en S3/R2/GCS (igual)

Soporte SQL multi-cliente
Sí (Trino, Spark, etc.)
Sí (DataFusion, Spark, Trino, Pandas)

Time-travel

Sí, vía ducklake_table_changes()

Branching de datasets
No nativo
Planeado para v2.0

Performance escrituras pequeñas
Baseline
~105× faster (Holanda)

Performance consultas
Baseline
~926× faster (Holanda)

Es importante calibrar los números. 926× más rápido en consultas no es la diferencia que vas a ver en un benchmark cualquiera; es el límite superior medido por el equipo en cargas con muchísimos archivos pequeños donde Iceberg sufre desproporcionadamente. En cargas batch tradicionales —ingesta horaria de millones de filas, sin updates intermedios— la diferencia se reduce a factores cercanos a la paridad, y Iceberg sigue siendo competitivo. La pregunta operativa para un equipo no es "qué formato es más rápido en abstracto" sino "qué formato encaja mejor con mi patrón de uso real".

Cómo empezar: implementación paso a paso

Para un equipo que quiere evaluar DuckLake hoy, el camino mínimo es directo.

Caso A: solo DuckDB local + filesystem

# Instalar DuckDB 1.5.2+ que incluye la extensión DuckLake
curl https://install.duckdb.org | sh

# Lanzar shell interactiva
duckdb
Enter fullscreen mode Exit fullscreen mode
-- Crear un DuckLake con catálogo SQLite + datos en filesystem local
ATTACH 'ducklake:/tmp/my_catalog.sqlite' AS lake (DATA_PATH '/tmp/data/');
USE lake;

CREATE TABLE customers (id INT, name VARCHAR, country VARCHAR);
INSERT INTO customers VALUES (1, 'Ana', 'SV'), (2, 'Bob', 'US');

SELECT * FROM customers;

-- Time-travel: ver cambios en la tabla
SELECT * FROM ducklake_table_changes('customers');
Enter fullscreen mode Exit fullscreen mode

Caso B: producción con catálogo PostgreSQL + datos en S3 / R2

-- Catalogo en Postgres, datos en R2 de Cloudflare
ATTACH 'ducklake:postgres://catalog_user:pwd@db.internal/lake_catalog'
  AS lake
  (DATA_PATH 's3://my-bucket/lake/',
   ENDPOINT 'https://abc123.r2.cloudflarestorage.com',
   ACCESS_KEY_ID 'XXX',
   SECRET 'YYY');

USE lake;

CREATE TABLE events (
  id BIGINT, ts TIMESTAMP, user_id VARCHAR, payload VARIANT
)
SET SORTED BY (ts DESC)
PARTITIONED BY BUCKET(user_id, 16);

-- Inserts pequeños se acumulan, descargas grandes se hacen automáticas
COPY events FROM 'recent_events.parquet';
Enter fullscreen mode Exit fullscreen mode

Caso C: lectura desde Apache Spark

from pyspark.sql import SparkSession

spark = (SparkSession.builder
    .config('spark.jars.packages', 'io.ducklake:ducklake-spark:1.0.0')
    .config('spark.sql.catalog.lake', 'io.ducklake.SparkCatalog')
    .config('spark.sql.catalog.lake.uri', 'postgres://...')
    .getOrCreate())

df = spark.sql("SELECT region, SUM(amount) FROM lake.sales GROUP BY region")
df.show()
Enter fullscreen mode Exit fullscreen mode

Caso D: queries analíticas desde Pandas (ad-hoc)

import duckdb

con = duckdb.connect()
con.execute("ATTACH 'ducklake:postgres://...' AS lake")

df = con.sql("SELECT * FROM lake.events WHERE ts > NOW() - INTERVAL 1 DAY").df()
# df es un DataFrame estándar de Pandas
Enter fullscreen mode Exit fullscreen mode

Casos de uso donde DuckLake brilla

Cargas con muchas escrituras pequeñas. Tablas de eventos donde cada producción de evento es una fila, sistemas de auditoría que registran cambios individuales, dashboards en tiempo real con upserts frecuentes.

Equipos que ya viven en SQL. Si el equipo tiene Postgres en producción gestionado por DBAs experimentados, agregar DuckLake encima reusa esa expertise. No hay que aprender Iceberg manifests, snapshots o compaction strategies.

Data engineering en el rango "no necesito Spark pero quiero ACID y time-travel". Equipos pequeños o medianos que tienen menos de 10 TB de datos pueden manejar todo el lakehouse con DuckDB local + Postgres como catálogo + R2/S3 para Parquet, sin necesidad de un cluster Spark, sin necesidad de Snowflake.

Multi-tenant SaaS con muchas tablas pequeñas. Cada tenant del SaaS es una tabla; el catálogo Postgres mantiene el listado, y el aislamiento es trivial. La proliferación de archivos en Iceberg con miles de tablas pequeñas es un problema operativo conocido; DuckLake lo elimina.

Casos de uso donde no es lo apropiado

Workloads de exclusivamente ingesta batch grande. Si tu pipeline es "una vez por hora ingesto 100 GB, después solo lecturas", Iceberg y Delta están bien optimizados y no hay ventaja material en migrar.

Ecosistemas Databricks / Snowflake con compromiso enterprise existente. Si la organización tiene contratos vigentes con Databricks o Snowflake, la integración nativa con Iceberg/Delta es probablemente más valiosa que la performance teórica de DuckLake.

Equipos sin DBA o expertise SQL operacional. Aunque DuckLake usa Postgres/SQLite, gestionar un Postgres en producción con backups, replicación y monitoreo es una habilidad real. Si el equipo no la tiene, los servicios manejados como Databricks pueden ser más razonables.

Lo que rompe en la 1.0

El PR #697 introdujo un breaking change que vale la pena conocer: adjuntar una versión más nueva de la extensión DuckLake ya no migra el catálogo automáticamente. La migración ahora es explícita, lo cual previene actualizaciones accidentales en producción pero exige un paso operativo adicional cuando se actualiza la extensión:

ALTER DUCKLAKE catalog UPGRADE;
Enter fullscreen mode Exit fullscreen mode

Esa es la única migración bloqueante respecto a 0.x. El resto del cambio es backward-compatible.

Roadmap: hacia dónde va DuckLake

El equipo publicó dos hitos próximos:

  • DuckLake v1.1 — Variant inlining cross-catalog, soporte para multi-deletion vector Puffin files, mejoras en data inlining para más tipos.
  • DuckLake v2.0Git-like branching para datasets (la feature más esperada por equipos de ML que necesitan experimentar sobre snapshots), role-based access control built-in en el catálogo, y mejoras en la primitiva de transacciones distribuidas.

El branching es particularmente interesante: permitirá crear una rama de un dataset, hacer cambios, validarlos contra otro pipeline, y mergear o descartar — el mismo modelo mental que devs ya tienen con Git, aplicado a millones de filas.

Posicionamiento estratégico: "cheating with a better design"

La frase que el equipo de DuckDB Labs usa para describir su enfoque es honesta. Mientras Iceberg y Delta Lake gastaron años construyendo sistemas distribuidos de metadata sobre object storage —resolviendo problemas duros de consistencia eventual, locks distribuidos y atomicidad sin servidor central—, DuckLake simplemente delega esos problemas a un RDBMS, donde llevan resueltos cuarenta años.

Es una decisión arquitectónica con tradeoffs reales: escalar verticalmente un Postgres es eventualmente un cuello de botella que un sistema basado en archivos no tiene. Pero para el 95% de los casos prácticos, donde una organización tiene menos de 1 PB de datos en un solo lakehouse y un Postgres bien configurado puede manejar millones de operaciones por segundo, la simplificación operacional es enorme.

Lección de fondo

Hay un patrón en la historia de la ingeniería de datos que vale la pena retener. Cada cinco o seis años aparece una propuesta que cuestiona una premisa que el campo daba por sentada. Hadoop cuestionó "los datos tienen que estar en una base de datos". Spark cuestionó "MapReduce es el único modelo de cómputo distribuido". Iceberg y Delta cuestionaron "los lakehouses no pueden tener ACID". DuckLake cuestiona "el catálogo de un lakehouse tiene que vivir en archivos".

La pregunta para quien evalúa estas tecnologías no es si la nueva propuesta es mejor en abstracto, sino si la premisa que cuestiona aplica a su caso real. Para muchos equipos, los problemas que Iceberg resolvió con archivos JSON en S3 nunca fueron problemas que ellos tuvieran. Para esos equipos, DuckLake representa una simplificación legítima. Para equipos al límite del scaling de Iceberg con cientos de millones de archivos, la conversación es distinta y los tradeoffs cambian.

Y para devs que están entrando al campo de data engineering en 2026: valdría más aprender DuckLake con un catálogo Postgres y datos en R2 que tratar de armar un cluster Spark + Iceberg + Hive Metastore desde cero. Es más cercano al stack que un equipo de tres a diez personas necesita, y el ecosistema Postgres es enormemente más maduro que cualquier sistema distribuido específico de lakehouse.

Fuentes

Top comments (0)