Skip to content

io_uring retains live heap on the only refapp with WebSocket and SSE routes: the sole failing cell in the 24h soak #573

Description

@FumingPower3925

The one cell that fails the soak leaks live memory, and it is the only one with streaming routes

The 24-hour soak (probatorium run 34368601903, pin c9f7dad) put 48 cells through an hour each. Forty-six report zero property violations. The two that do not are the same cell on both architectures — auth_session_ratelimit on io_uring — and they fail only I-MEM-1, the heap-growth oracle. The same reference app on epoll and on std is clean on both hosts, and every other io_uring cell is clean.

host cell heap_inuse over the hour violations
msa2 (amd64) auth_session_ratelimit / io_uring 11.2 → 40.9 MB 2550
msr1 (arm64) auth_session_ratelimit / io_uring 9.0 → 29.1 MB 2476
msa2 auth_session_ratelimit / epoll 3.1 → 11.8 MB 0
msa2 auth_session_ratelimit / std 1.7 → 6.8 MB 0

Magnitude is not what separates them. observability on io_uring grew further in its own hour, 17.9 → 53.5 MB, and passed. The oracle fits post-warm-up per-150-second minima, so a workload that ramps and then plateaus scores flat however high the plateau sits. What fails is a series whose post-collection floor keeps rising.

It is live memory, not span fragmentation

I reproduced the cell locally under Docker against the same pin and sampled exact runtime.MemStats from the reference app once a second, then re-derived the oracle's own statistic from the series. At eighteen minutes:

series post-collection trough slope
HeapInuse 1,987 B/s — over the 1,024 B/s budget, same verdict as the cluster
HeapAlloc 1,334 B/s
HeapObjects 5.7 /s

Live object count rising is the part that matters. Fragmentation moves HeapInuse without moving HeapAlloc or HeapObjects; all three move here. Roughly 234 bytes per retained object, against about 71 accepted connections per second.

What is distinctive about this cell

auth_session_ratelimit is the only one of the nine reference apps that routes /ws and /events. The other eight have no WebSocket and no server-sent events at all, and the validator probes for those routes and skips the corresponding walker slices when they are absent. All three engines take the identical streaming load in this cell — 47,598 WebSocket upgrades and 9,185 event streams killed mid-stream per hour, matched to within five requests across engines — and only io_uring retains.

So the intersection is: io_uring, plus connections that detach.

What this is not

  • Not goroutines. The failing cell has the smallest goroutine growth of any io_uring cell in the run (+15 over the hour), and I-MEM-3 is zero everywhere. The incident's goroutine profile holds 101 goroutines, two of them in the event-stream handler and none in WebSocket.
  • Not connection accumulation. Active connections sit at 44 at the incident, with accepted minus closed accounted for.
  • Not the session store. Sessions expired exceeds sessions created over the window, and the same middleware runs on the engines that stay clean.
  • Not the reference app's own bookkeeping. Its WebSocket handler is a bare echo loop and its event handler a ticker; neither registers the connection anywhere.

Still open

Which allocation site. The default heap profiler samples one allocation per 512 KB, which cannot resolve this rate over any window shorter than the cell itself — the soak's own incident dossier ships two profiles whose difference is smaller than the profiler's sampling error, which is why it names nothing (probatorium#319). Two controls and a finer-grained profiling run are in flight:

  1. epoll on the identical local cell, to confirm the asymmetry off-cluster
  2. the same reference app with /ws and /events renamed away, so the streaming walkers are skipped and everything else is byte-identical
  3. a repeat io_uring cell with the profiler sampling every 4 KiB, which resolves the growth well enough for a profile diff to name the site

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions