Skip to content

fix: defer pipelined frames that can't fit (stop empty-page degradation under load)#4

Merged
poly-glot merged 2 commits into
mainfrom
fix/processframes-pipeline-defer
Jun 6, 2026
Merged

fix: defer pipelined frames that can't fit (stop empty-page degradation under load)#4
poly-glot merged 2 commits into
mainfrom
fix/processframes-pipeline-defer

Conversation

@poly-glot

Copy link
Copy Markdown
Owner

Problem

Under the 300/500/1000-VU sweep, the backend returned err (status=4) for browse/search/getCategoriesByIds (no crash), and the frontend rendered HTTP-200 pages with no categories (35% at 300 VU). Root cause: processFrames packs many responses into one 256KB buffer but only guarded 10 bytes before dispatch, so tail frames in a batch got a too-small slice and the bounds-checked handlers shed them with .err.

Fix

Dispatch a frame only if the buffer is empty (first frame → full 256KB) or the remainder can hold a worst-case all-or-nothing response (get_categories_by_ids = 200 × @sizeOf(Category)). Otherwise stop the batch; the leftover frame is reprocessed on a fresh buffer by finishBinaryWrite's existing drain loop (verified). No error, no hang, no per-handler changes.

Also: k6 name tag to fix metric cardinality.

Verified

zig build test exit 0 in Linux + Zig 0.15.2 container (incl. new pipelineHasRoom test). Will re-run the 300/500/1000 sweep post-deploy to confirm empty-page rate → ~0.

poly-glot added 2 commits June 6, 2026 10:04
processFrames packs many responses into one 256KB per-connection buffer.
The only pre-dispatch guard checked for a 10-byte header, so under load a
frame landing in a near-full tail got a too-small slice — the bounds-checked
handlers (browse/search/get_categories_by_ids) then returned 'err', which the
frontend turned into empty/degraded pages (200 OK, no categories). Same
buffer-pressure condition that crashed handleStats.

Now a frame is dispatched only if the buffer is empty (first frame gets the
whole buffer) or the remainder can hold a worst-case all-or-nothing response
(get_categories_by_ids: 200 categories). Otherwise the batch stops and the
leftover frame is reprocessed on a fresh buffer by finishBinaryWrite's existing
drain loop — no error, no hang. Test locks the reserve threshold.
Dynamic category/search URLs produced ~169 unique name/url tag values, which
Grafana Cloud flags as high cardinality. Set a static name (home/category/search)
per request type so each metric collapses to one series.
@poly-glot
poly-glot merged commit 20cc4cf into main Jun 6, 2026
2 checks passed
@poly-glot
poly-glot deleted the fix/processframes-pipeline-defer branch June 6, 2026 12:29
poly-glot added a commit that referenced this pull request Jun 7, 2026
fix: defer pipelined frames that can't fit (stop empty-page degradation under load)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant