.agents/skills/tinybird/rules/endpoint-optimization.md
Use this checklist when optimizing endpoints.
Before optimizing, collect evidence from these sources:
pipe_stats_rt: Query SELECT * FROM tinybird.pipe_stats_rt WHERE pipe_name = 'endpoint_name' to check execution duration percentiles (p50, p90, p95, p99), read_bytes, rows_read, and error counts.?explain=true (e.g., https://$TB_HOST/v0/pipes/endpoint_name?explain=true) to inspect join strategies, aggregation stages, index usage, and partition pruning.Ignore datasources with fewer than 10,000 rows or less than 50 MB of data.
Schema, query-shape, or data-layout issues. Apply whenever detected — no runtime evidence needed.
SELECT * or unused columns increase I/O, decompression cost, and cache pressure.LowCardinality for low-unique strings, defaults instead of Nullable.Nullable adds overhead from null bitmaps and extra checks.Nullable(T) with T when the column never contains nulls.ORDER BY reduce sparse index effectiveness and data skipping.ORDER BY with low-cardinality and/or time columns. Avoid timestamp as first key in multi-tenant cases.String values when only metadata is needed wastes memory and CPU.Apply only when runtime thresholds are exceeded, based on pipe_stats_rt and EXPLAIN data.
EXPLAIN, or memory > 60%, or OOM/timeout errors.EXPLAIN, or memory spikes, or OOM/timeout errors.ORDER BY aligned to selective filters.EXPLAIN shows late filtering.PREWHERE.EXPLAIN.EXPLAIN shows ineffective pruning.GROUP BY.FINAL by enforcing correctness at ingestion time (lambda architecture).visitParamExtract*/JSONExtract* calls), see the single-parse JSON pattern in materialized-files.md — it applies here too, but matters most in materialized views since the parse cost compounds over every ingested row.COUNT(DISTINCT) with p95 > 5s, or memory > 50%, or OOM errors.uniqHLL12 or similar approximate functions when acceptable.tinybird.pipe_stats_rt and tinybird.pipe_stats.system.query_logMaterialized view:
NODE materialized_view_name
SQL >
SELECT toDate(timestamp) as date, customer_id, countState(*) as event_count
FROM source_table
GROUP BY date, customer_id
TYPE materialized
DATASOURCE mv_datasource_name
ENGINE "AggregatingMergeTree"
ENGINE_PARTITION_KEY "toYYYYMM(date)"
ENGINE_SORTING_KEY "customer_id, date"
Optimized query:
NODE endpoint_query
SQL >
%
SELECT date, sum(amount) as daily_total
FROM events
WHERE customer_id = {{ String(customer_id) }}
AND date >= {{ Date(start_date) }}
AND date <= {{ Date(end_date) }}
GROUP BY date
ORDER BY date DESC