Summary
A long-lived Rayforce service reports 3.76 GB allocated against 11.4 MB of
reachable data, growing ~10 MB/min indefinitely. .sys.gc reclaims 32 KB of
it. A second service on the same build, same IPC subscription path and a
larger working set is clean, so this is not simply how the allocator behaves.
I could not reduce it to a primitive. What follows is the evidence and
everything I ruled out, in case it is enough for you to recognise.
The process
An incremental consumer: subscribes to a multicast topic, buffers arriving
rows in a global table, and every 5 s runs a pass of group-bys, joins and
anti-joins over what arrived, appending results to a findings table that is
trimmed to a 100k-row cap.
(.sys.mem)
bytes-allocated : 3,764,364,736 (3.76 GB)
peak-bytes : 3,787,658,624
alloc-count : 69,584,761
sys-current : 4,330,172,416
anon-committed : 4,328,521,728
(.mem.objsize findings) : 5,374,456 (5.4 MB, 94k rows)
(.mem.objsize depth) : 5,967,396 (6.0 MB, ~10k rows, oscillating)
(.mem.objsize last_msg) : 38,374 (38 KB, 774 rows)
(.sys.env) : 407 globals
11.4 MB reachable through globals; 3.76 GB allocated. RSS 4.1 GB. Uptime 7 h.
It is retained, not uncollected
before (.sys.gc) : 3,766,785,344
after (.sys.gc) : 3,766,752,576 (32,768 bytes reclaimed)
It grows linearly, while the working set does not
Sampled 60 s apart:
| bytes-allocated |
alloc-count |
findings rows |
depth rows |
| 3,763,437,824 |
69,648,121 |
94,280 |
11,355 |
| 3,772,720,320 |
69,819,874 |
94,451 |
7,944 |
| 3,783,627,264 |
70,004,906 |
94,769 |
9,501 |
~10 MB/min, ~3,000 allocations/s, with both tables flat. That rate over the
uptime accounts for the whole 4.1 GB.
A control on the same build
Another service of ours, same Rayforce, also a multicast subscriber, with a
much larger working set (534k-row table), 98 min uptime:
bytes-allocated : 124 MB
(.mem.objsize levels) : 44 MB
(.mem.objsize output) : 50 MB
124 MB against 94 MB visible. No gap, no growth. So the IPC/subscribe path and
table mutation in general are not implicated.
Ruled out
Each run in a loop against tables of the shape the service uses (50k rows,
773 distinct symbol keys), with .sys.gc before and after, measuring
bytes-allocated delta. All reported 0 MB:
anti-join on a two-column key, 200 reps
inner-join on a two-column key, 200 reps
select {by: ...} group-by, 200 reps
select {where: ...}, 200 reps
xasc, 100 reps
concat, 200 reps
insert into a global then trim with a negative take, 300 reps
- reassigning a global from an
anti-join, 300 reps
delete {from: 'global where: ...}, 300 reps
format with 3 and 7 arguments, 20,000 reps
map over til, 20,000 reps
at on a column, 20,000 reps
So the leak is not in those operations standalone. What the real service adds
over the probes is that it runs them from a timer callback, on data arriving
through a multicast subscription, continuously for hours -- rather than in a
tight loop in one evaluation.
What would help
If there is a counter or a debug build that attributes live allocations by
call site, I am happy to run it against this process and report back -- it is
reproducible simply by leaving the service up. Equally, if alloc-count minus
frees is tracked anywhere, that delta would localise it quickly.
Measured on rayforce 2.6.2.r229.ge7770412.
Summary
A long-lived Rayforce service reports 3.76 GB allocated against 11.4 MB of
reachable data, growing ~10 MB/min indefinitely.
.sys.gcreclaims 32 KB ofit. A second service on the same build, same IPC subscription path and a
larger working set is clean, so this is not simply how the allocator behaves.
I could not reduce it to a primitive. What follows is the evidence and
everything I ruled out, in case it is enough for you to recognise.
The process
An incremental consumer: subscribes to a multicast topic, buffers arriving
rows in a global table, and every 5 s runs a pass of group-bys, joins and
anti-joins over what arrived, appending results to a findings table that is
trimmed to a 100k-row cap.
11.4 MB reachable through globals; 3.76 GB allocated. RSS 4.1 GB. Uptime 7 h.
It is retained, not uncollected
It grows linearly, while the working set does not
Sampled 60 s apart:
~10 MB/min, ~3,000 allocations/s, with both tables flat. That rate over the
uptime accounts for the whole 4.1 GB.
A control on the same build
Another service of ours, same Rayforce, also a multicast subscriber, with a
much larger working set (534k-row table), 98 min uptime:
124 MB against 94 MB visible. No gap, no growth. So the IPC/subscribe path and
table mutation in general are not implicated.
Ruled out
Each run in a loop against tables of the shape the service uses (50k rows,
773 distinct symbol keys), with
.sys.gcbefore and after, measuringbytes-allocateddelta. All reported 0 MB:anti-joinon a two-column key, 200 repsinner-joinon a two-column key, 200 repsselect {by: ...}group-by, 200 repsselect {where: ...}, 200 repsxasc, 100 repsconcat, 200 repsinsertinto a global then trim with a negativetake, 300 repsanti-join, 300 repsdelete {from: 'global where: ...}, 300 repsformatwith 3 and 7 arguments, 20,000 repsmapovertil, 20,000 repsaton a column, 20,000 repsSo the leak is not in those operations standalone. What the real service adds
over the probes is that it runs them from a timer callback, on data arriving
through a multicast subscription, continuously for hours -- rather than in a
tight loop in one evaluation.
What would help
If there is a counter or a debug build that attributes live allocations by
call site, I am happy to run it against this process and report back -- it is
reproducible simply by leaving the service up. Equally, if
alloc-countminusfrees is tracked anywhere, that delta would localise it quickly.
Measured on
rayforce2.6.2.r229.ge7770412.