Saltar al contenido principal

El carril de consultas wasm

La mayoría de las consultas se ejecutan de nuestro lado: envías SQL, lo autorizamos, lo ejecutamos, y devolvemos filas tipadas. El carril wasm invierte eso. Pides un plan en lugar de filas — Parquet prefirmado, un archivo por conjunto de datos, más el SQL que hay que ejecutar sobre él — y la consulta se ejecuta en el navegador del visitante a través de DuckDB-WASM.

Este es el carril detrás de un panel que no necesita ninguna credencial: sin clave de API, sin token de incrustación, sin servidor propio. Es también el carril con el compromiso más afilado de toda la plataforma, y ese compromiso no está escondido en ningún sitio — es el mismo hecho que «ningún secreto que filtrar», leído del otro lado.

Cómo pedirlo​

Un argumento en la llamada de consulta selecciona el carril.

from vibedasher import Vibedasher

client = Vibedasher(api_key="...")

result = client.query(sql="SELECT region AS dimension, SUM(price) AS value FROM sales GROUP BY 1", type="wasm")

if result.mode == "wasm":
run_locally(result.plan) # plan.sql + plan.sources
else:
print(result.rows, result.fallback_reason)

Por MCP es la misma elección, escrita run_query(sql, type='wasm').

type es "backend" (el valor por defecto) o "wasm". Todo lo que está por encima de la elección del carril no cambia: la autorización, la validación solo por alias y la inyección de filtros siguen ocurriendo en el servidor, antes de la comprobación de capacidad. Pedir wasm no se salta ninguna puerta. Un conjunto de datos que la clave no puede alcanzar se rechaza antes de que se materialice ningún extracto o se firme ninguna URL.

Ramifica sobre mode, nunca sobre lo que pediste​

Pedir wasm no garantiza obtenerlo. Si algún conjunto de datos participante no es apto para wasm, la consulta se ejecuta en el servidor exactamente como siempre — y lo dice en lugar de fallar.

CampoSignificado
modeLo que realmente se ejecutó: "backend" o "wasm". Este es el campo sobre el que ramificar.
fallbackReasonSe define solo cuando se pidió type="wasm" y se rechazó, nombrando por qué.
planSe define solo cuando mode == "wasm".
columns / rowsVacíos cuando mode == "wasm" — no se computó nada.
La tabla da la ortografía del protocolo. El SDK de Python los expone en snake_case
(result.fallback_reason), el cliente TypeScript en camelCase, y los parámetros de las
herramientas MCP van en snake_case en todas partes — así que type es type en cualquier caso,
pero fallbackReason se escribe fallback_reason en Python.

Un repliegue silencioso sería el peor comportamiento posible aquí: tu código recibiría filas, creería estar en el carril sin credenciales, y enviaría un build que necesita una credencial que no tiene. Así que el repliegue nunca es silencioso, y la razón siempre se nombra.

Qué contiene un plan​

{
"mode": "wasm",
"plan": {
"sql": "SELECT region AS dimension, SUM(price) AS value FROM sales GROUP BY 1",
"sources": [
{ "alias": "sales", "datasetId": 12, "url": "https://...", "bytes": 4194304 }
]
}
}
  • plan.sql es el SQL inyectado — filtros aplicados, parámetros ya escapados como literales. No es tu SQL crudo, y ahí está el punto: ambos carriles computan resultados idénticos, así que un panel no puede significar una cosa servida desde nuestro motor y otra cosa servida desde el navegador.
  • sources[].alias es el cleanSQLName del conjunto de datos — exactamente el nombre de tabla que plan.sql referencia, y el nombre bajo el que el archivo Parquet debe registrarse en local. El archivo ya trae nombres de columna limpios.
  • sources[].url está prefirmada y es de solo lectura. Es descargable desde cualquier origen, y el endpoint del plan también lo es.

Ejecutar un plan es: descargar cada url, registrarla en DuckDB-WASM como una vista llamada alias, ejecutar plan.sql. Un panel expulsado sobre este carril trae un runner que hace exactamente eso y nada más; su única dependencia es @duckdb/duckdb-wasm, que está en npm.

Tienes que pedir el extracto antes de que exista​

Esta es la única regla de secuencia, y saltársela produce un bucle que no termina nunca.

Pedir type="wasm" es lo único que construye el extracto wasm de un conjunto de datos. El extracto se materializa de forma perezosa, por esa petición — o por la vía de actualización después de que un conjunto de datos queda listo — y en ningún otro sitio.

Así que si la expulsión informa de dataLane.id == "token" con la razón no-wasm-extract, el arreglo es ejecutar una consulta por conjunto de datos nombrado con type="wasm" — cualquier SELECT trivial contra el alias servirá — y luego expulsar de nuevo. Un plan devuelto significa que el extracto ya existe; para este propósito puedes ignorar por completo el contenido del plan.

Publicar no cuece un extracto. «Volver a publicar y volver a expulsar» dará vueltas para siempre.

El carril anónimo, y por qué no está en la referencia de la API​

El carril de arriba necesita una clave de API, porque eres tú construyendo un panel. Lo que llama el navegador de un visitante es un endpoint distinto, sin autenticar: el plan wasm de la visualización, en

GET /v1/public/viz/{account_slug}/{viz_slug}/wasm-plan

Está deliberadamente ausente de la referencia de API generada y de los dos SDK, y esa ausencia es una decisión de diseño más que una omisión. La ruta es anónima y orientada al navegador; no hay llamada con clave de API para ella, así que no tiene sitio en un SDK de clave de API. Está excluida a nivel del framework, que es por lo que no la encontrarás navegando la referencia — la única descripción que existe de ella es esta página y el runner que la llama.

Sus guardas son idénticas a las de la ruta de caché anónima que está a su lado: la cuenta debe existir y permitir paneles públicos, y la visualización debe ser pública y estar publicada. Un panel privado o protegido responde 404 aquí. Nada de ello es un valor por defecto del lado cliente que puedas sobrescribir.

Devuelve { sources: [...], unavailable: [...] }. Un conjunto de datos que no es apto para wasm se reporta en unavailable con una razón en lugar de desaparecer silenciosamente de sources — porque un cliente que recibiera una lista sources corta y ninguna explicación renderizaría un panel parcial y lo daría por terminado.

Por qué un panel protegido nunca puede servirse de esta forma​

Las filas de un panel protegido se acotan por visor reescribiendo el SQL en el servidor. Un archivo Parquet prefirmado es la tabla entera. No hay forma de honrar un predicado de filas que el cliente simplemente podría negarse a ejecutar — así que el carril no está «aún no implementado» para paneles protegidos; está estructuralmente excluido, y lo estará siempre.

Qué estás sacrificando​

Dilo antes de construir, porque es la mitad que no puedes cambiar después.

El extracto está indexado por conjunto de datos e instantánea, no por tu consulta. El mismo SELECT con y sin WHERE devuelve el archivo idéntico. Tu predicado no lo encoge. Consecuencias, todas ellas la misma consecuencia:

  • Contiene todas las filas de cada conjunto de datos que lee el panel.
  • Es un archivo, el mismo archivo, para todo el que abra la página.
  • La página no emite ninguna consulta al servidor, así que nada en la ruta podría dar a un visor menos filas que a otro.
  • Un filtro aplicado en el navegador cambia lo que se dibuja, nunca lo que se entregó — y los bytes están en la pestaña de red en cualquier caso.
  • Sirve la instantánea publicada, no una consulta viva. Refrescar lo que muestra significa volver a publicar.

A cambio: ninguna clave que filtrar, ningún token que renovar, ningún backend propio que ejecutar o pagar, ningún límite de tasa delante de la página, y un panel que sigue renderizando desde un host estático.

También vale saberlo: el límite de filas no se aplica aquí. El carril del lado servidor se detiene en 100.000 filas y pone truncated, lo que hace silenciosamente erróneo cualquier total calculado desde el prefijo. El Parquet del carril wasm es el conjunto de datos completo por instantánea, precisamente porque está indexado por conjunto de datos y no por tu resultado — así que es también la respuesta honesta a un conjunto de resultados genuinamente grande, no solo a un problema de credenciales.

Cuándo elegir otra cosa​

NecesitasCarril
filas que vas a leer y sobre las que razonarrun_query / client.query — el carril backend por defecto
muchas filas en discorun_query_to_file(output_path=...)
un panel público sin credencial y sin backendwasm
que visores distintos vean filas distintasun token de incrustación con alcance limitado que lleve filters, o código de servidor confiable que acote el SQL
datos vivos en lugar de una instantánea publicadael carril backend, detrás de un servidor que tú controlas

Un cliente con token de alcance limitado rechaza type="wasm" de plano en lugar de ejecutarse calladamente en el servidor: quien pidiera wasm y obtuviera filas de todos modos no tendría forma de notarlo. Ese rechazo es el mismo principio que fallbackReason — un cambio de carril nunca debe ser invisible.

Para los dos montajes que sí pueden acotar a un visor, mira Filtrado a nivel de fila.