Tracing a 3 Memory Blow-Up in Grafana's Time Comparison
While contributing to Grafana, I picked up a memory issue in the Time Comparison feature — a follow-up to earlier performance work I had done in the same area. A comparison panel was consuming significantly more memory than expected. The interesting part: the extra memory wasn't coming from real data. This post covers how I traced it to the root cause and fixed it. Background Time Comparison overlays an earlier period onto the current one — for example, this week vs. last week. The comparison data is fetched from the earlier window and shifted forward before rendering: Query → DataFrame → Prepare frame → Shift → Render │ └─ Gap filling The important detail: gap filling ran before the comparison frame was shifted. The Problem I reproduced the issue with: Parameter Value Series 500 Window 6h Interval 20s Compare offset 24h A single-period panel contained roughly 540,000 points , so a comparison panel should be about 2× the baseline . Instead, the compare frame contained 3,240,500 points — ~6× the baseline — and consumed 76.4 MB . The question was: where did the extra points come from? Investigation I first verified the baseline to rule out the query returning unexpected data. It was correct. Then I used a reproducible browser harness and a heap snapshot to inspect the extra memory. Most of it was null rows introduced during gap filling — not real samples, not copies. Following the frame through the preparation pipeline revealed why. When gap filling ran, the compare frame still represented data 24 hours in the past , but the gap-filler was using the current time range as its reference: Compare frame Current range [===== 6h =====] [===== 6h =====] └─────────────── 24h ───────────────┘ gap-filler reads this offset as one gap At a 20-second interval, 24 hours is: 24 × 60 × 60 / 20 = 4,320 intervals So up to 4,320 null positions per series were introduced purely because the frame hadn't been shifted yet. The frame was then shifted forward, leaving most of that padding out