Skip to content

perf: release oversized S2 cache backing buffers - #947

Merged
szibis merged 8 commits into
mainfrom
perf/compact-stats-cache-buffers
Oct 31, 2026
Merged

szibis merged 8 commits into
mainfrom
perf/compact-stats-cache-buffers

Conversation

@szibis

@szibis szibis commented Oct 31, 2026 •

Copy link
Copy Markdown
Collaborator

S2-compressed stats extents could retain their worst-case output backing allocation in RAM, queued disk writes and peer jobs while being charged only for compressed length. Copy output with excess capacity into an exact-length owned buffer before cache admission. This releases retained capacity; a capacity-only reslice would not.

Stored bytes, keys, tenant/auth scope, deadlines, invalidation, placement and logical budgets are unchanged. No new dependency or storage engine. Release metadata #946 is incorporated; only this fix remains under Unreleased.

Main versus PR

Measured main fe43377f2ac78bdd4069d5ad5220f1a6ceeb6007; implementation 74900fedce7ee537900895979096adbdbd3a77fc; fleet candidate 8e848caf743b6c96c0b9561b7c1d24c07a0fc05e. Later commits add wiring, evidence and release metadata, without changing measured production code. Both fleet binaries have clean source identities. Go1.27.3/Darwin/arm64/M5Pro; benchmark GOMAXPROCS4.

Controlled memory fixture: 256 independent extents, 10,000 points each, three fresh-process trials. Values are medians; cache and fixture remain alive through two GCs and ReadMemStats.

Metric Main PR
Encoded bytes per extent 50,291 B Identical bytes
Retained backing capacity per extent 180,070 B 50,291 B (-72.1%)
Post-GC retained heap, RAM fixture 44.10 MiB 14.10 MiB (-68.0%)
Heap per entry, RAM plus pending disk queue 180,803 B 57,902 B (-68.0%)
Logical RAM charge per entry, including key 50,320 B Same
Cold encode, compressible extent 24 allocs, 1.039 MiB/op 25 allocs, 1.093 MiB/op
Cold encode, small extent 4 allocs, 704 B/op 5 allocs, 832 B/op
Empty encoding 1 alloc, 32 B/op Same
Warm decoder 24 allocs, 1.575 MiB/op Same

Measured retained memory

Cost: one additional compressed-length copy/allocation whenever excess capacity exists. The 50,291-byte copy consumes a 57,344-byte allocator class. Transient serialization/S2 scratch remains; heap savings are not a hard process limit or a fleet RSS reduction. No encoding/decoding latency difference is statistically significant in the retained ABBA comparison; timing controls vary. The earlier whole-handler control also has varying allocations and is retained as diagnostic only. No warm-query speedup claim.

Fleet and backend work

Isolated Loki3.7.9/VL1.52.0, same immutable192,000-line fixture and identical query/cell/ring identities; fresh processes/cache files and separate source ledgers. Three proxies, RF1/2, Tier0off, primary64MiB/1,024 entries per node, logical disk64MiB, S2,1s/1,000-entry flush,16MiB queue. Backend/reference-Loki caches are shared and may already be warm. Twenty measured queries per cell.

State RF Main p50 / p95 ms PR p50 / p95 ms VL calls main / PR Peer calls main / PR
exact 1 0.179 / 0.240 0.160 / 0.205 0 / 0 0 / 0
exact 2 0.226 / 0.294 0.128 / 0.160 0 / 0 0 / 0
shifted 1 6.007 / 7.143 3.945 / 4.812 40 / 40 13 / 13
shifted 2 4.672 / 6.752 3.921 / 5.145 40 / 40 27 / 27
refresh 1 6.546 / 7.826 4.146 / 5.359 40 / 40 13 / 13
refresh 2 6.155 / 7.872 4.515 / 7.027 40 / 40 26 / 26
disk restart 1 1.330 / 2.073 1.277 / 1.575 0 / 0 13 / 13
disk restart 2 0.945 / 1.687 0.936 / 1.743 0 / 0 7 / 7
node0 down 1 1.453 / 1.777 1.129 / 1.914 0 / 0 10 / 10
node0 down 2 0.826 / 0.958 0.833 / 1.010 0 / 0 0 / 0

All400/400 answers are nonempty200 and equal Loki. Sequential instrumented-loopback timings are descriptive, not proof this memory change caused a query speedup.

Total across200 measured requests per source Main PR
Proxy process CPU counter deltas 360.712 ms 329.561 ms
VL calls / response-body bytes 160 / 42,586 B Same
Peer contacts 109 Same
Loki primary L1 hits/lookups 40/200 Same
VL primary L1 / L2 / L3 hits/lookups 110/190 · 50/80 · 30/30 Same
Loki primary encoded disk reads/writes 0 / 0 B Same
VL primary encoded disk reads/writes 14,050 / 2,063 B Same
Largest sampled alive-process cache-file sum 294,912 B Same

Exact repeats bypass VL lookup; disabled Tier0 is not a measured miss. Shift/refresh reuses held buckets; each missing minute is fetched with two VL calls, severity-source discovery followed by stats_query_range, both bounded to that minute. Disk writes can be delayed priming work, not measured-query admissions. CPU includes background process work. File lengths and encoded traffic are not physical SSD IOPS/throughput. Raw per-layer occupancy, hits/lookups, eviction counters, request ranges and separate priming/startup observations are retained.

The node-loss cells stop node0 but do not force every queried key to belong to it; they do not prove RF1 can lose its sole copy without VL. Pressure, forced-owner loss, distributed deletion, fleet scaling, VL-server CPU/RSS and physical device I/O are not newly proven. Darwin's process-memory export is GoSys, not RSS; kernel lifetime Maxrss is separately scoped in raw cells.

Full provenance, raw compressed cells/wire logs, pprof, byte hashes, controls, reproduction and independent verifier: retained proof.

Validation and visual evidence

  • New ownership and raw/S2/zstd disk-queue regressions reproduce main-red/candidate-green; verify bytes, metadata, subsequent-encode isolation and restart deadlines.
  • Fresh root suite:21,355 passing test/subtest events across13 test-bearing packages,zero failures. Focused stats extent race and disk/peer restart-codec race pass;96,365 fuzz executions pass. New encoding fuzzer is wired into the CI12s campaign.
  • Real Loki/VL extent-cache e2e passes on both sources, six query shapes across repeats/shifts/refresh/widening; asserts actual extent hit/partial outcomes. Existing TestCompat_.* CI shard covers it.
  • Lint0, configured gosec0/type-errors0, actionlint,40 CI wiring tests, changelog and conformance pass. Independent source/security/measurement review. CI-pinned Gitleaks8.28 scans70 commits with no leaks; one historical browser-prose false positive is scoped to its exact fingerprint, current metadata is structured and future content remains scanned.
  • All12 local proxy variants rebuilt/healthy at source378a08b, existing backend volumes retained, fresh logs verified and patterns500-series limit published. Candidate24h/48h browser regressions pass9.6s with populated charts/visible partial notices/no API or page errors; screenshots and deployment proof.
  • No Grafana-visible answer change: byte-identical encoding/decoding and exact API tests cover this memory ownership change. The memory chart proves the particular improvement. CI's nine general main/PR/Loki Visual smoke scenes remain selected; hosted current-head publication/checks are pending.

@github-actions github-actions Bot added size/XL Extra large change scope/proxy Proxy core scope/ci CI/CD scope/docs Documentation scope/tests Tests performance Performance labels Oct 31, 2026
@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

⚪ Performance A/B: no change beyond noise

8 shape×range — 8 ⚪ same · results vs Loki: 7 same, 0 new difference(s), 1 pre-existing

Loki profiles compared (like for like):

  • aligned: not run (no build supports -align-queries-with-step, so every aligned row would be not like-for-like)
  • loki-defaults: 8 shape×range; proxy flag base off, pr off

Every shape is within noise of the base build (±30% and 100 ms).

8 unchanged shape×range (within noise)
shape range Loki profile base p50 PR p50 change Loki p50 PR ÷ Loki result vs Loki
⚪ drilldown service landing 1h defaults 16 ms 13 ms ⚪ -19% 226 ms 0.06× ✅ same
⚪ instant ungrouped rate instant defaults 21 ms 23 ms ⚪ +10% 73 ms 0.32× ✅ same
⚪ json rate by level (no filter, no drop) 1h defaults 400 400 ⚪ +50% 400 — ✅ same
⚪ json volume no filters 1h defaults 66 ms 68 ms ⚪ +3% 138 ms 0.49× ✅ same
⚪ logs plain 1h defaults 75 ms 78 ms ⚪ +4% 609 ms 0.13× ✅ same
⚪ logs | json 1h defaults 77 ms 77 ms ⚪ +0% 88 ms 0.88× ✅ same
⚪ stream-label count by app 1h defaults 7 ms 7 ms ⚪ +0% 105 ms 0.07× ✅ same
⚪ topk rate by app 1h defaults 14 ms 17 ms ⚪ +21% 182 ms 0.09× ➖ differs (as base)
Cold (first-run) timings — what a user opening a panel sees
shape range Loki profile base cold PR cold Loki cold PR ÷ Loki cold
drilldown service landing 1h defaults 41 ms 34 ms 467 ms 0.07×
instant ungrouped rate instant defaults 20 ms 21 ms 80 ms 0.26×
json rate by level (no filter, no drop) 1h defaults 400 400 400 —
json volume no filters 1h defaults 149 ms 166 ms 824 ms 0.20×
logs plain 1h defaults 152 ms 77 ms 547 ms 0.14×
logs | json 1h defaults 178 ms 177 ms 1.95 s 0.09×
stream-label count by app 1h defaults 18 ms 15 ms 424 ms 0.04×
topk rate by app 1h defaults 20 ms 19 ms 707 ms 0.03×
Why these shapes
  • control smoke → 8: control/drilldown service landing, control/instant ungrouped rate, control/json rate by level (no filter, no drop), control/json volume no filters, control/logs plain, control/logs | json, control/stream-label count by app, control/topk rate by app

base 28a5a2f699 → PR 6e7db1ba84 · 4 runs (first is cold), warm p50 · ranges 1h · noise ±30% and 100 ms · VictoriaLogs restarts 0→0 · 1.6 min · phases stack_up 39s, seed_and_build 22s, loki-defaults:loki_profile 0s, loki-defaults:proxies 2s, loki-defaults:matrix 17s. Same runner, one fresh stack, targets interleaved per request. How to read and reproduce: bench/ab/README.md.

@github-actions github-actions Bot added size/XL Extra large change and removed size/XL Extra large change labels Oct 31, 2026
@github-actions github-actions Bot added size/XL Extra large change and removed size/XL Extra large change labels Oct 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Visual smoke: passed

Base 28a5a2f699 vs PR f6bcc05117, Grafana through the proxy builds and Loki on one fresh stack; run.
Core set: 9 capture(s) at 1h. Detailed set: 0 entries (nothing visual touched). 9 capture(s), 15m/1h/6h, 24h and 7d stay out of PR runs.

page range set base = PR (data) vs Loki (PR) pixel diff result
dd-fields 1h core ✅ identical identical (4 explained, 1 history-dependent) 0.11% ✅
dd-landing 1h core ✅ identical identical (3 explained) 0.14% ✅
dd-service-logs 1h core ✅ identical identical (3 explained, 1 history-dependent) 0.11% ✅
explore-logs-plain 1h core ✅ identical identical (1 explained) 0.07% ✅
explore-metric-plain 1h core ✅ identical identical (1 explained) 0.04% ✅
explore-sum-by-over-binary 1h core ✅ identical identical (1 explained) 0.04% ✅
explore-topk-by-per-step 1h core ✅ identical identical (1 explained) 0.04% ✅
explore-unwrap-sum 1h core ✅ identical identical (1 explained) 0.04% ✅
lvl-explore-rate-errors-by-service 1h core ✅ identical 1 difference(s), 1 on base too; 1 explained 0.04% ✅

Gate: a base-vs-PR data difference passes when the PR is closer to Loki, and fails when the PR diverges from Loki or Loki cannot decide it (at a range Loki does not hold, a difference passes when the same page proved that query to answer like Loki at a shorter range; otherwise label the pull request visual-change-expected to accept an intended change). Also failing: a panel empty on the PR but not on the base, an error banner, panel error, error answer or new "No data" panel on the PR, a PR side that never settled, a value difference that did not reproduce on the recapture. A delayed first-pass request and pixel differences only warn.

Core set montages (9): base | PR | Loki

dd-fields-1h

dd-fields-1h

dd-landing-1h

dd-landing-1h

dd-service-logs-1h

dd-service-logs-1h

explore-logs-plain-1h

explore-logs-plain-1h

explore-metric-plain-1h

explore-metric-plain-1h

explore-sum-by-over-binary-1h

explore-sum-by-over-binary-1h

explore-topk-by-per-step-1h

explore-topk-by-per-step-1h

explore-unwrap-sum-1h

explore-unwrap-sum-1h

lvl-explore-rate-errors-by-service-1h

lvl-explore-rate-errors-by-service-1h

Published inline montages: pr-visuals/pr-947. Full montage set: artifact.

@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

PR Quality Report

Compared against base branch main.

Coverage and tests

Signal Base PR Delta
Test count 21349 21362 13
Coverage 89.6% 89.6% 0.0% (stable)

Compatibility

Track Base PR Delta
Loki API 100.0% 11/11 (100.0%) 0.0% (stable)
Logs Drilldown 100.0% 17/17 (100.0%) 0.0% (stable)
VictoriaLogs 100.0% 11/11 (100.0%) 0.0% (stable)

Performance smoke

Lower CPU cost (ns/op) is better. Lower benchmark memory cost (B/op, allocs/op) is better. Higher throughput is better. Lower load-test memory growth is better. Benchmark rows are medians from repeated samples.

Signal Base PR Delta
QueryRange cache-hit CPU cost 3571.0 ns/op 3551.0 ns/op -0.6% (stable)
QueryRange cache-hit memory 1256.0 B/op 1256.0 B/op 0.0% (stable)
QueryRange cache-hit allocations 28.0 allocs/op 28.0 allocs/op 0.0% (stable)
QueryRange cache-bypass CPU cost 4211.0 ns/op 4280.0 ns/op +1.6% (stable)
QueryRange cache-bypass memory 1323.0 B/op 1322.0 B/op -0.1% (stable)
QueryRange cache-bypass allocations 28.0 allocs/op 28.0 allocs/op 0.0% (stable)
Labels cache-hit CPU cost 946.2 ns/op 925.0 ns/op -2.2% (stable)
Labels cache-hit memory 48.0 B/op 48.0 B/op 0.0% (stable)
Labels cache-hit allocations 3.0 allocs/op 3.0 allocs/op 0.0% (stable)
Labels cache-bypass CPU cost 1561.0 ns/op 1555.0 ns/op -0.4% (stable)
Labels cache-bypass memory 400.0 B/op 400.0 B/op 0.0% (stable)
Labels cache-bypass allocations 14.0 allocs/op 14.0 allocs/op 0.0% (stable)

State

  • Test count and coverage were collected for this PR.
  • Compatibility and sampled performance are reported here from the same PR workflow.
  • This is a delta report, not a release gate by itself. Required checks still decide merge safety.
  • Performance is a smoke comparison, not a full benchmark lab run.
  • Delta states use the same noise guards as the quality gate (percent + absolute + low-baseline checks), so report labels match merge-gate behavior.

@szibis
szibis merged commit 4750bdd into main Oct 31, 2026
74 checks passed
@szibis
szibis deleted the perf/compact-stats-cache-buffers branch October 31, 2026 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance Performance scope/ci CI/CD scope/docs Documentation scope/proxy Proxy core scope/tests Tests size/XL Extra large change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants