Published

F7·b · Approach — Trino como motor principal del SQL Editor (lectura Y escritura)

Connect any source, model it as an ontology, transform it, and operationalize it, analytics, automation and machine learning, under one governed, self-hostable roof. --- Most teams stitch the...

F7·b · Approach — Trino como motor principal del SQL Editor (lectura Y escritura)

Decisión del owner 2026-08-08: Carbon SQL es Trino SQL; el SQL Editor debe leer y
escribir el Warehouse por Trino, y DuckDB sale del mapa.

Este documento dice qué falta entre hoy y eso, con las mejores prácticas del
ecosistema contrastadas contra nuestro stack. Ordenado por lo que BLOQUEA, no por
lo que apetece.

Cruza con: federated-catalog-wedge.md ·
platform-thesis.md §D-bis ·
trino-absorcion.md


0 · Dónde estamos hoy, medido

SQL Editor  →  runQuery (Junction)  →  getSqlEngine('duckdb')  →  DuckDB  →  Iceberg/R2
                                       ↑ lib/compute/junction.ts:1534, CABLEADO A FUEGO

Lectura y DML, los dos. Trino no aparece en el path, y no podría: su adaptador declara write: false y su credencial acuñada es de sólo lectura (gate ⑨ de F2: un INSERT devuelve 403 desde R2).


1 · ⛔ LOS DOS BLOQUEANTES DUROS

① Vercel no alcanza a Trino. Punto.

El TrinoCluster es listenerClass: cluster-internal y no publica ningún puerto. El SQL Editor corre en Vercel. No hay configuración que arregle eso: hay que exponer el motor.

La práctica del ecosistema, y coincide con lo que ya teníamos apalabrado en F6:

«Best practice is to instead use an external load balancer for managing TLS
connections rather than securing the coordinator directly.»
— doc de Trino, TLS

LB externo termina TLS, con certificado de verdad, y detrás el coordinador. Las piezas ya están casi todas: la IP 34.78.181.113 reservada, trino.paladio.io resolviendo, y JWT ya configurado (F3·a). Falta el balanceador y el certificado.

⚠️ Y JWT exige TLS + shared secret, que ya tenemos (internalSecretClass). Nada que rehacer ahí.

② 🔴 Trino 470 tiene un CVE que se dispara EXACTAMENTE al dar escritura

GHSA-x27p-5f68-m644

Quélas credenciales de storage se serializan en el JSON de la query, accesible por UI/API
Privilegio necesarioescritura a nivel SQL — literalmente lo que este documento quiere habilitar
Qué se exponecredenciales estáticas y vended
Versiones afectadas439 → 479
Corregido en480
Nosotros470 ⚠️

Habilitar escritura sobre nuestro Trino actual entrega las credenciales de R2 a cualquiera con permiso de escritura. No es teórico y no se mitiga configurando.

La salida, verificada: el operador Stackable 25.3 sólo tiene 451/455/470, pero SDP 26.7 trae Trino 481 — por encima de 479, o sea parcheado, y por encima de 463, o sea que conserva los namespaces anidados que nuestro main.default exige.

Actualizar el operador 25.3 → 26.7 es prerrequisito de la escritura, no una mejora opcional. Y es una fase propia: cambian CRDs y puede moverse la superficie de configuración (F1 ya nos enseñó que los campos se mueven entre versiones).


2 · Lo que hay que hacer después, y ninguno es trivial

③ La credencial acuñada tiene que poder escribir

Hoy Lakekeeper le acuña a Trino una credencial de sólo lectura — medido, no supuesto (gate ⑨). Escribir exige cambiar el grant y el vending. Ojo al orden con el ②: dar escritura ANTES de parchear el CVE es la peor combinación posible.

iceberg.unique-table-location = 'true' — obligatorio para nosotros

Lakekeeper lo documenta como necesario cuando el soft-delete está activo, para que una tabla recreada no choque con la que está esperando su expiración. Nuestro soft-delete está activo (7 días). Sin esta línea, un DROP + CREATE de la misma tabla rompe — y el fallo aparece días después, cuando expira la vieja.

⑤ Junction: dejar de cablear el motor a fuego

// lib/compute/junction.ts:1534
const engine = getSqlEngine('duckdb');

Es una línea, pero cambiarla por getSqlEngine('trino') sería repetir el error. Ese punto es la POLÍTICA de la puerta: debe seleccionar según capacidad declarada (supportsFeatures) y disponibilidad (available), no según una constante. Si no, el día que Trino no esté, el editor cae entero en vez de degradar.

⑥ El contrato del control-plane — lo que de verdad protege la invariante

El DML de DuckDB pasa por withDmlControlPlane, que estampa dataset_transactions e iceberg_sync_log. La invariante real del Warehouse no es «un solo escritor»: es «nadie escribe esquivando la puerta». Ya hay dos escritores legítimos (ml-runner en la ingesta, DuckDB en el DML) y los dos pasan por ahí.

⇒ El DML de Trino tiene que entrar por runQuery con writeTarget, igual que DuckDB. Si commitea directo contra el catálogo, el oráculo de frescura se queda ciego y la transacción no existe para nadie.

(Y hay un detalle a favor: DuckDB no puede estampar snapshot_properties en su commit Iceberg — limitación medida. Trino sí escribe propiedades de snapshot, así que la trazabilidad mejora con el cambio.)

⑦ Las 8 divergencias, ahora al revés

Con Carbon SQL = Trino SQL, dejan de ser «a Trino le faltan 8» y pasan a ser «nuestras superficies emiten dialecto DuckDB y tienen que emitir Trino». Las 5 de traducir pasan a «la superficie deja de emitirlo»; qualify y distinct-on hay que reescribirlas; y timestamp-arithmetic es la que no avisa. Ver lib/compute/dialect-divergence.ts.

⑧ ⚠️ El protocolo de Trino NO encaja con una función serverless

El protocolo de cliente de Trino es un bucle de sondeo: POST /v1/statement → seguir nextUri hasta que deje de venir. Una función de Vercel tiene tope de duración; una query larga sobrevive a la petición HTTP que la lanzó, y el cliente muere a mitad del bucle.

Nuestro trino-client.ts hace hoy el bucle en proceso — correcto para queries cortas y equivocado como diseño general.

La forma correcta: no sostener la petición abierta. Enviar la query, devolver el queryId, y sondear desde el cliente (o desde un worker durable). Es, además, lo que permite cancelar y lo que hace que un editor SQL se sienta como un editor SQL.

⑨ La latencia manda más aquí que en ningún otro sitio

Medido: la ida y vuelta al catálogo por nuestra cara cuesta ~3,4 s por query, y no es el dato — es la topología (GKE europe-west1 → Vercel → Railway → R2 WEUR). Para batch da igual; para un editor interactivo es la diferencia entre usable e inusable. Co-locar la cara y el catálogo con el motor deja de ser optimización y pasa a ser requisito de esta fase.


3 · La secuencia, y por qué en este orden

FasePor qué va aquí
1Actualizar Stackable 25.3 → 26.7 (Trino 470 → 481)cierra el CVE ANTES de que exista un solo permiso de escritura. Y hacerlo con el motor todavía en sólo-lectura es la ventana barata
2Exponer Trino — LB + certificado + trino.paladio.io (F6/F3·b)sin esto Vercel no habla con el motor y nada de lo demás se puede probar e2e
3Lectura por Trino en el SQL Editor — selección de motor real en Junction (⑤) + las divergencias de dialecto (⑦)lectura primero: si algo diverge, se ve sin haber tocado un byte
4Escritura: vending con permiso (③) + unique-table-location (④) + DML por runQuery con writeTarget (⑥)sólo cuando 1-3 estén verdes
5Retirar DuckDBlo último, y sólo cuando 4 lleve tiempo verde

La regla que ordena todo: el CVE antes que el permiso, la exposición antes que
la prueba, la lectura antes que la escritura, y la retirada la última.
Cualquier otro
orden crea una ventana en la que algo está mal y no se nota.


4 · Lo que este approach NO resuelve

  • No decide si el editor debe cancelar queries — que es lo que hace útil el ⑧, y es producto, no infraestructura.
  • No dimensiona la concurrencia. El SQL Editor es interactivo y multiusuario; el coordinador es uno. Eso es Gateway/flota (F6) y hay que medirlo antes de prometerlo.
  • No aborda la identidad del usuario final en el catálogo. trino#27917 sigue abierto: Trino habla con Lakekeeper como Trino, no como el usuario. OPA aplica la política del usuario en el motor; el catálogo sigue viendo un único principal técnico. Es una capa, no dos, y con escritura habilitada conviene tenerlo escrito.