La evolución de las bases de datos ha oscilado entre arquitecturas in-process y cliente-servidor. Mientras que las bases de datos in-process como SQLite y DuckDB ofrecen una latencia mínima para aplicaciones embebidas y análisis interactivo, presentan desafíos inherentes para la concurrencia multi-proceso y la gestión de estado mutable compartido. La necesidad de soportar casos de uso como la ingesta de telemetría concurrente y dashboards en tiempo real, que requieren múltiples escritores y lectores sobre el mismo archivo de base de datos, ha impulsado la creación de Quack.

Este desarrollo no solo expande el alcance de DuckDB más allá de su nicho inicial, sino que también subraya un principio fundamental en el diseño de sistemas distribuidos: la elección de la arquitectura de comunicación (in-process vs. red) es un trade-off directo entre latencia/simplicidad y concurrencia/escalabilidad. Quack busca cerrar esta brecha, permitiendo a DuckDB funcionar como un componente central en arquitecturas de datos modernas, sin abandonar su filosofía de facilidad de uso y rendimiento.

Históricamente, la introducción de arquitecturas cliente-servidor en los años 80 por Sybase marcó un cambio paradigmático, separando el estado mutable de la lógica de aplicación. Quack reintroduce esta separación para DuckDB, pero con una implementación moderna que aprende de las deficiencias de protocolos más antiguos y las fortalezas de los más recientes, como Arrow Flight SQL, al tiempo que optimiza para los patrones de acceso específicos de DuckDB.

Arquitectura del Sistema

El protocolo Quack se construye sobre HTTP, aprovechando la madurez y optimización de la pila de red para la transmisión eficiente de mensajes. Opera bajo un patrón de solicitud-respuesta, donde el cliente inicia todas las interacciones. Los mensajes Quack, como solicitudes de conexión y ejecución de consultas, se codifican utilizando el tipo MIME application/duckdb.

La serialización de datos y estructuras complejas, como tipos de datos y conjuntos de resultados, se realiza mediante las primitivas de serialización internas de DuckDB. Estas primitivas, ya utilizadas en componentes críticos como el Write-Ahead Log (WAL), están optimizadas para eficiencia y han sido probadas en batalla. Esta decisión permite a DuckDB mantener el control sobre la evolución de sus formatos internos sin depender de estándares externos como Apache Arrow para la serialización en el cable.

Para la seguridad, Quack genera un token de autenticación aleatorio por defecto al iniciar el servidor y se enlaza solo a localhost. Aunque no usa SSL por defecto para comunicaciones locales, recomienda el uso de un proxy HTTP como Nginx para la terminación SSL al exponer el servicio a la red pública. El modelo de autenticación y autorización es extensible, permitiendo a los usuarios reemplazar las funciones de callback predeterminadas con lógica personalizada, como la integración con directorios LDAP o la implementación de políticas de autorización basadas en consultas SQL. Una optimización clave es la capacidad de manejar una consulta completa con una sola ida y vuelta (round-trip) una vez establecida la conexión, crucial para entornos sensibles a la latencia, y la optimización para la transferencia masiva de resultados en paralelo.

Flujo de Conexión y Consulta Quack

  1. 1 Cliente DuckDB Instala y carga la extensión Quack. Crea un secreto de autenticación.
  2. 2 Servidor DuckDB Instala y carga la extensión Quack. Llama a quack_serve() con token y bind ad...
  3. 3 Cliente DuckDB ATTACH a la instancia remota usando 'quack:hostname' y el token.
  4. 4 Cliente DuckDB Envía una solicitud de conexión HTTP con el token de autenticación.
  5. 5 Servidor DuckDB Valida el token usando el callback de autenticación.
  6. 6 Cliente DuckDB Envía una solicitud HTTP para ejecutar una consulta SQL.
  7. 7 Servidor DuckDB Ejecuta la consulta, serializa el resultado usando primitivas internas.
  8. 8 Servidor DuckDB Envía la respuesta HTTP con el resultado serializado.
CapaTecnologíaJustificación
networking HTTP Protocolo de capa de aplicación para la comunicación cliente-servidor, elegido por su ubicuidad, optimización de pila y facilidad de integración con infraestructura existente (balanceadores de carga, firewalls, proxies SSL). vs Arrow Flight SQL, Protocolo PostgreSQL, gRPC Puerto por defecto 9494; bind a localhost por defecto; recomendación de proxy HTTP (e.g., Nginx) para terminación SSL en conexiones externas.
security Tokens de autenticación Mecanismo de autenticación por defecto, generado aleatoriamente al inicio del servidor para asegurar el acceso. El modelo es extensible mediante callbacks. vs SSL/TLS por defecto (rechazado para localhost), Autenticación basada en usuarios/roles (extensible) Generación de token aleatorio; callback de autenticación configurable por el usuario; callback de autorización configurable por el usuario.
data-processing Primitivas de serialización internas de DuckDB Codificación eficiente de solicitudes y respuestas, incluyendo tipos de datos y conjuntos de resultados. Permite a DuckDB mantener el control sobre la evolución de sus formatos internos y optimizar el rendimiento. vs Apache Arrow (rechazado por dependencia externa y diseño de round-trips) MIME type `application/duckdb`.

Trade-offs

Ganancias
  • Concurrencia multi-proceso
  • ▲▲ Rendimiento en transferencia masiva de datos
  • Rendimiento en transacciones pequeñas
  • Flexibilidad de despliegue (cliente-servidor)
Costes
  • Latencia de red (comparado con in-process)
  • Complejidad de infraestructura (requiere gestión de red/seguridad)
DuckDB #1
INSTALL quack FROM core_nightly;
LOAD quack;
CALL quack_serve(
'quack:localhost',
token = 'super_secret'
);
CREATE TABLE hello AS
FROM VALUES ('world') v(s);

DuckDB #2
INSTALL quack FROM core_nightly;
LOAD quack;
CREATE SECRET (
TYPE quack,
TOKEN 'super_secret'
);
ATTACH 'quack:localhost' AS remote;
FROM remote.hello;
Ejemplo básico de cómo configurar un servidor Quack en una instancia de DuckDB y cómo un cliente se conecta y consulta una tabla remota.

Fundamentos Teóricos

El problema fundamental que Quack aborda se relaciona con la gestión de la concurrencia y la consistencia en sistemas de bases de datos distribuidos, un tema central en la investigación de bases de datos desde sus inicios. La necesidad de un protocolo para coordinar el acceso a un estado compartido por múltiples procesos remotos se alinea con los principios de los sistemas transaccionales distribuidos. Conceptos como el control de concurrencia optimista o pesimista, y la serialización de transacciones, son fundamentales aquí.

La decisión de DuckDB de operar inicialmente in-process, similar a SQLite, refleja un enfoque que prioriza la simplicidad y el rendimiento local, evitando la sobrecarga de la comunicación de red y los desafíos de la consistencia distribuida. Sin embargo, la introducción de Quack es una respuesta a la demanda de casos de uso que requieren la semántica de un sistema cliente-servidor, donde la gestión del estado mutable en un único punto (el servidor DuckDB) simplifica la lógica de consistencia para los clientes, aunque introduce latencia de red. Este trade-off es un tema recurrente en papers sobre arquitecturas de bases de datos, como los trabajos seminales sobre sistemas de gestión de bases de datos distribuidas y la evolución de los protocolos de comunicación de bases de datos, que exploran cómo minimizar la sobrecarga de la red y maximizar el throughput en entornos distribuidos.