Explain what Grafana panel transformations are, where in the query lifecycle they execute, and when you should push the same work into the query instead of doing it as a transformation.
answer
- ordered pipeline, output feeds next stage
- runs in the browser, after the query
- join by field = align two queries on time
- filter-by-value is display-only, not a cost control
- push aggregation down, keep correlation up
basics
~20 sTransformations are an ordered, per-panel pipeline that reshapes the data frames a data source already returned — join, reduce, filter, rename, group. They run in the browser after the query, so they cost rendering time and never reduce what was fetched.
solid answer
~50 sA transformation is a stage in a **per-panel, ordered pipeline** applied to the data frames the data source already returned: the output of stage 1 is the input of stage 2. Common stages are *Join by field* (align two queries on time or a label), *Organize fields* (rename, hide, reorder), *Reduce* (series → summary rows), *Filter data by name/query/value*, *Group by*, *Labels to fields*, *Add field from calculation*, and *Partition by values*. The key mechanical facts: they execute **client-side, after the query returns**, they belong to one panel and cannot be shared, and they are the only way to correlate results from two different queries or two different data sources inside a panel. Push work down into the query whenever the backend can do it — aggregation, label filtering, downsampling, top-N. A transformation that discards 90% of the rows still paid to transfer and parse 100% of them, and the reduction happens in the user's browser rather than once in the backend.
code
text · 6 linespipeline A: frames -> Filter by value (status=500) -> Reduce (mean)
=> mean of error rows only
pipeline B: frames -> Reduce (mean) -> Filter by value (status=500)
=> mean of everything, then a filter over summary rows
(the status column may not even survive the reduce)go deeper
Know that transformations reshape query results inside a panel — join, rename, reduce, filter — and that they run after the query.
Explain the ordered pipeline and the long-versus-wide reshaping, and give an example where transformation order changes the answer.
Argue the push-down boundary with cost reasoning: per-viewer browser work versus once-in-the-backend aggregation, and why correlation across sources is the legitimate client-side case.
Treat per-panel pipelines as duplicated logic across a dashboard estate, and weigh fixing shape at the source (recording rules, views, schema) against dozens of hand-maintained pipelines.
## Where transformations sit The lifecycle of a panel render is: query options are resolved (time range, interval, max data points) → the data source returns **data frames** → **transformations** run in order → **field configuration and overrides** are applied (units, thresholds, mappings, display names) → the panel draws. Transformations therefore see raw frames and hand shaped frames to the visualisation. They cannot influence the query, and they cannot see anything the query did not return. They are stored per panel. Two panels needing the same reshaping duplicate the pipeline; there is no shared transformation library. This is a real maintenance cost on large dashboards and is a legitimate argument for fixing shape at the source instead. ## The pipeline model The list is ordered and each entry consumes the previous entry's frames. Order changes results, which is the single most common source of confusion: - *Filter by value* then *Reduce* summarises only the surviving rows; the reverse order summarises everything and then filters summaries. - *Join by field* before *Organize fields* means you rename the joined columns; after, you rename the pre-join ones (and your renames may vanish). Grafana shows the frames entering and leaving each stage in the transformation editor's debug view, and the panel inspector's Data tab shows the final result — that pair is how you actually debug a pipeline rather than guessing. ## The stages that matter - **Join by field**: an outer or inner join on a shared field, almost always `time`. This is how you put two queries in one table, or compute a ratio between two series. Beware: joins on time only align rows whose timestamps are byte-identical, so two queries at different steps produce a sparse, null-riddled table. Aligning the step (min interval, or the same interval macro) is the fix. - **Organize fields**: rename, hide, reorder. Purely cosmetic, but it is what turns a raw SQL result into a readable table. - **Reduce**: turn each series into one row of statistics (last, mean, max, count). This is how you build a "current value per instance" table from time-series data. - **Filter data by query / by name / by value**: keep one query's frames, keep matching fields, or keep rows matching a predicate. Filter-by-value is row-level and runs in the browser, so it is a display filter, never a security or cost control. - **Labels to fields / Group by / Partition by values**: reshape between the *long* form (one row per timestamp+label combination) and the *wide* form (one column per series) that most panels prefer. Many "this panel refuses my data" problems are exactly a long/wide mismatch. - **Add field from calculation**: binary maths between two fields (a ratio, a difference), a unary function, or a row-wise reduction. Useful when the backend query language cannot express the arithmetic across two separate results. - **Config from query results**: use one query's output to set another's display config — data-driven thresholds, units or display names. ## Cost and where it lands Everything here happens in the browser tab, on data already transferred. That gives three practical rules: 1. **Transformations never reduce load on the backend or on the network.** A panel that fetches 200k points and reduces them to 12 rows still fetched 200k points. 2. **Cost scales with points × series.** Heavy pipelines on wide results are a common cause of a dashboard that is slow after the requests have finished. 3. **They are per-viewer.** The same reduction is recomputed in every open browser, whereas a backend aggregation (a `GROUP BY`, a `sum by`, a recording rule) is computed once and cached. ## Push-down decision Do it in the query when the backend can express it: aggregation, label/dimension filtering, top-N, time bucketing, downsampling, arithmetic within one query. Do it as a transformation when the backend cannot: correlating **separate** queries, correlating **different data sources**, cosmetic field work, converting between long and wide, and shaping a result specifically for one panel type. One boundary worth naming explicitly in an interview: transformations are a *visualisation-time* feature and belong to the dashboard. Server-side evaluation — for example alert rules — uses its own expression pipeline rather than panel transformations, so a reduction you rely on in a panel does not exist for anything evaluated without a browser.
- You join two queries on time and the resulting table is full of nulls. What happened and how do you fix it?A join on the time field matches only identical timestamps, and the two queries were evaluated at different steps or offsets, so most rows have a value from one side and a null from the other. Fix it upstream: give both queries the same interval (a shared Min interval, or the same interval macro), or bucket both to the same resolution in the query. Failing that, aggregate to a coarser bucket before joining.
- A colleague filters sensitive rows out of a table with a Filter data by value transformation. Is that acceptable?No. The filtering happens in the browser after the full result has been fetched, so the sensitive rows are present in the response and visible in the panel inspector and the network tab. Row-level restriction has to happen where the data is served — a scoped query, backend-enforced tenancy, or row-level security — not in a display transformation.
saying these in an interview costs you the question
- Claiming transformations reduce the amount of data fetched or the load on the data source
- Treating the transformation list as unordered
- Using Filter by value as an access-control or cost-control mechanism
- Rebuilding an aggregation in the browser that the query language could do server-side
- Assuming a panel's transformations also apply to anything evaluated outside the browser