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 acontent/(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:autocachea resultados de query keyed por nombre de query y combinación de parámetro;livedeclara el model deliberadamente sin cache (usado para views siempre frescas como balances live — omitettl_secondscon él). Dos modos más (cache_only,stale_while_revalidate) son aceptados por el schema pero actualmente se comportan comoauto.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 nodocache:con las mismas keysmode/ttl_secondspara 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.