Docs / Build Workflow

Cache de Queries

Por qué importa el cache

Cada carga de dashboard dispara queries Malloy contra tu source — un scan facturado en BigQuery, round-trips de base de datos en Postgres o MySQL. Sin cache, las mismas agregaciones corren en cada page view — lo que significa que costo y latencia escalan con el tráfico, no con qué tan seguido cambian tus datos en realidad.

Para la mayoría de los workloads analíticos, los datos cambian una vez al día o menos. Agregar un cache con un TTL razonable hace que los dashboards sean rápidos para los usuarios y predecibles en costo para ti.

Cómo funciona: el archivo sidecar

El cache se configura por model usando un archivo sidecar al lado del archivo .malloy. El sidecar tiene el mismo nombre que el model, con sufijo .cache.yml:

content/
  models/
    ec_revenue.malloy
    ec_revenue.cache.yml    ← cache config for ec_revenue.malloy
    ec_performance.malloy
    ec_performance.cache.yml

Si no existe sidecar, las queries corren live en cada request.

Working example

El shape de producción declara la política una vez bajo defaults y lista las views cacheables bajo queries — una key por view, cada una con un cuerpo vacío {} que significa "heredar los defaults":

# content/models/ec_revenue.cache.yml

model: models/ec_revenue.malloy

defaults:
  cache:
    mode: auto
    ttl_seconds: 1800

queries:
  over_time: {}
  by_category: {}
  top_categories: {}
  by_brand: {}

Campo por campo:

  • model: path al model al que aplica esta config de cache, relativo a content/ (ej. models/ec_revenue.malloy). La fuente de verdad de la plataforma es el filename del sidecar junto al model; este campo es un label de auto-referencia que la validación chequea contra un archivo real.
  • defaults.cache.mode: auto cachea resultados de query keyed por nombre de query y combinación de parámetro; live declara el model deliberadamente sin cache (usado para views siempre frescas como balances live — omite ttl_seconds con él). Dos modos más (cache_only, stale_while_revalidate) son aceptados por el schema pero actualmente se comportan como auto.
  • defaults.cache.ttl_seconds: cuánto tiempo los resultados cacheados son válidos. Después de este tiempo, el siguiente request dispara una query fresh y repobla el cache. 1800 = 30 minutos.
  • queries: una key por nombre de view. El cuerpo de una query también puede llevar su propio nodo cache: con las mismas keys mode / ttl_seconds para override de los defaults en esa view; en la práctica, los sidecars de producción mantienen cada cuerpo en {} y parten los models cuando las views necesitan políticas distintas.

Un sidecar siempre-live:

model: models/wallet_balances.malloy

defaults:
  cache:
    mode: live

queries:
  balance_summary: {}

Elegir un TTL

Matchea TTL a qué tan seguido cambian los datos subyacentes:

  • Pipelines batch diarios: ttl_seconds: 86400 (24 horas)
  • Refreshes por hora: ttl_seconds: 3600 (1 hora)
  • Near real-time: saltea el cache o usa ttl_seconds: 300 (5 minutos)
  • Reportes y dashboards document: ttl_seconds: 1800 (30 minutos) es un default seguro

Setear un TTL muy corto en queries pesadas no las hace más frescas — solo las hace caras. Matchea TTL al SLA real de freshness de los datos, no a qué tan seguido los usuarios abren el dashboard.

Cache y filtros de dashboard

Las cache keys incluyen los parámetros de query pasados por los filtros del dashboard. Un usuario filtrando por "2024" obtiene un resultado cacheado para esa combinación específica de parámetro. Un usuario filtrando por "2023" dispara una entry de cache separada la primera vez, después hace hit al cache en cargas posteriores.

Esto significa que dashboards con muchas combinaciones distintas de filtro tienen un costo de warm-up de cache más grande. Para dashboards document con una fecha default fija, el cache típicamente está warm dentro de una carga.

Qué invalida una entry cacheada

Cada entry cacheada está scoped a una query en un model con una combinación de parámetro específica. Editar el archivo .malloy invalida cada entry cacheada para queries adentro en el siguiente request — no hay paso de eviction manual.

Diferencias entre adapters

El comportamiento del cache en sí es el mismo en los tres adapters. Las implicancias de costo difieren:

  • BigQuery — cada run sin cache es un scan facturado; favorecer TTLs más largos en queries estables domina la ecuación de costo.
  • Postgres y MySQL — el costo es mayormente latencia per-roundtrip; el cache ayuda menos cuando las tablas subyacentes son chicas y están bien indexadas.

Mira la comparación de adapters de source.