Skip to content

events: split index.pack's big terms by slab, read only the window's slabs - #1082

Draft
tamirms wants to merge 2 commits into
tamirms/events-pack-byte-limitfrom
tamirms/events-split-terms
Draft

tamirms wants to merge 2 commits into
tamirms/events-pack-byte-limitfrom
tamirms/events-split-terms

Conversation

@tamirms

@tamirms tamirms commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

A popular term's bitmap is read whole for every page that touches it: on
pubnet chunk 6410 the fifteen most popular terms are 250 KiB to 1.08 MiB
each, so a popular page reads about 9 MB of index to answer from a few
slabs of it. And index.pack's 128-entry records make its tail follow the
term count, which no fixed first read covers on big chunks, so every open
there pays a second round trip.

A term whose serialized bitmap is over 64 KiB is now stored as one entry
per slab of the chunk, zero-length where it has no events, and LookupKeys
reads only the slabs the window reaches and reports their span as
covered. The app data names the split terms after the chunk's slab
count, one 8-byte row of slot and fingerprint each, and the blob has an
exact length. The stamp keeps its version, since nothing has shipped:
index packs written before this change have to be rebuilt. Records close at 16 KiB with no
entry limit, so the tail follows the file size, and Open's first read is
max(64 KiB, 4 bytes per 16 KiB of file), rounded up to 4 KiB.

Every stage of a query after the first now asks for at most 16 slabs
rather than the whole rest of the window. A split term is read only for
a stage's slabs, at most one container per slab, so a query holds at
most 128 KiB of each split term at once whatever the chunk's density,
under 2 MiB for queryEvents' 15-term budget.

When a query has a split term, each stage's answer covers only that
stage's slabs, so the next stage asks for every term again. The cold
reader keeps each whole term it decodes and hands later stages of the
same request that bitmap instead of reading and decoding it again. A
cold reader serves one request, so what it keeps is bounded by that
request's terms, and callers already treat lookup results as
read-only. One request's staged lookups of one split term and 14 whole
terms just under 64 KiB, with the files in the page cache, took 1.0 ms
and allocated 3 MiB instead of 3.3 ms and 15 MiB on 156 slabs (pubnet
density, 11 stages), and 4.4 ms and 10 MiB instead of 33 ms and
115 MiB on 916 slabs (six times that density, 58 stages).

Measured on gp3 with a cold page cache, against the code before this
change: five popular terms over four slabs took 1.8 ms p50 instead of
11.9 ms on a pubnet chunk folded to seven times its density, and 12 ms
instead of 118 ms with eight query streams each reading its own copy. A
rare term took 3.0 ms instead of 5.3 ms on sac-6000, whose tail now fits
one read. A read of a split term's whole chunk rebuilds the term from
its slab entries, about 0.5 ms more CPU per term on the folded chunk
than decoding one whole bitmap.

A page that never fills, on the folded chunk with its 16 most popular
terms paired so that they never occur together, holds 2.2 MB at a time
rather than 37 MB, and 200 such pages take 0.61 GB of resident memory
rather than 13.9 GB. Alone and warm it takes 96 ms instead of 82 ms;
under load its throughput and total CPU are the same.

🤖 Generated with Claude Code

@tamirms
tamirms added this pull request to stack #1081 October 5, 2026 13:32
@tamirms
tamirms removed this pull request from stack #1081 October 5, 2026 16:19
@tamirms
tamirms force-pushed the tamirms/events-split-terms branch from 9c001bd to b7c7396 Compare October 5, 2026 16:19
Base automatically changed from tamirms/events-windowed-matches to tamirms/events-pack-byte-limit October 5, 2026 16:19
@tamirms
tamirms added this pull request to stack #1089 October 5, 2026 16:24
@tamirms
tamirms removed this pull request from stack #1089 October 8, 2026 08:18
@tamirms
tamirms force-pushed the tamirms/events-split-terms branch from b7c7396 to 9dd7c16 Compare October 8, 2026 08:18
@tamirms
tamirms added this pull request to stack #1102 October 8, 2026 08:19
@tamirms
tamirms force-pushed the tamirms/events-split-terms branch from 9dd7c16 to d6850e4 Compare October 8, 2026 08:34
@tamirms
tamirms force-pushed the tamirms/events-split-terms branch 3 times, most recently from 54bd67e to 2f48a12 Compare October 9, 2026 14:28
@tamirms
tamirms force-pushed the tamirms/events-split-terms branch 2 times, most recently from 486acc2 to f07e219 Compare October 15, 2026 19:15
@tamirms
tamirms force-pushed the tamirms/events-split-terms branch from f07e219 to f3ef45d Compare October 19, 2026 13:00
@tamirms
tamirms force-pushed the tamirms/events-split-terms branch 5 times, most recently from ec8b39b to bfa3418 Compare October 21, 2026 11:16
@tamirms
tamirms force-pushed the tamirms/events-split-terms branch from 420e116 to 5f84830 Compare October 21, 2026 14:43
@tamirms
tamirms force-pushed the tamirms/events-split-terms branch from 5f84830 to be8a0bb Compare October 21, 2026 17:05
@tamirms
tamirms force-pushed the tamirms/events-split-terms branch from be8a0bb to 06cf851 Compare October 21, 2026 17:15
@tamirms
tamirms force-pushed the tamirms/events-split-terms branch 3 times, most recently from 67cd29e to bfb8832 Compare October 23, 2026 05:27
@tamirms
tamirms force-pushed the tamirms/events-split-terms branch 4 times, most recently from 5b10819 to d3e7b89 Compare October 24, 2026 02:49
@tamirms
tamirms removed this pull request from stack #1102 October 27, 2026 10:19
@tamirms
tamirms added this pull request to stack #1157 October 27, 2026 10:21
@tamirms tamirms moved this from To Do to In Progress in Platform Scrum Oct 27, 2026
@tamirms
tamirms force-pushed the tamirms/events-split-terms branch from d3e7b89 to 67dde93 Compare October 28, 2026 08:05
tamirms and others added 2 commits November 4, 2026 10:02
…slabs

A popular term's bitmap is read whole for every page that touches it: on
pubnet chunk 6410 the fifteen most popular terms are 250 KiB to 1.08 MiB
each, so a popular page reads about 9 MB of index to answer from a few
slabs of it. And index.pack's 128-entry records make its tail follow the
term count, which no fixed first read covers on big chunks, so every open
there pays a second round trip.

A term whose serialized bitmap is over 64 KiB is now stored as one entry
per slab of the chunk, zero-length where it has no events, and LookupKeys
reads only the slabs the window reaches and reports their span as
covered. The app data names the split terms after the chunk's slab
count, one 8-byte row of slot and fingerprint each, and the blob has an
exact length. The stamp keeps its version, since nothing has shipped:
index packs written before this change have to be rebuilt. Records close at 16 KiB with no
entry limit, so the tail follows the file size, and Open's first read is
max(64 KiB, 4 bytes per 16 KiB of file), rounded up to 4 KiB.

Every stage of a query after the first now asks for at most 16 slabs
rather than the whole rest of the window. A split term is read only for
a stage's slabs, at most one container per slab, so a query holds at
most 128 KiB of each split term at once whatever the chunk's density,
under 2 MiB for queryEvents' 15-term budget.

Measured on gp3 with a cold page cache, against the code before this
change: five popular terms over four slabs took 1.8 ms p50 instead of
11.9 ms on a pubnet chunk folded to seven times its density, and 12 ms
instead of 118 ms with eight query streams each reading its own copy. A
rare term took 3.0 ms instead of 5.3 ms on sac-6000, whose tail now fits
one read. A read of a split term's whole chunk rebuilds the term from
its slab entries, about 0.5 ms more CPU per term on the folded chunk
than decoding one whole bitmap.

A page that never fills, on the folded chunk with its 16 most popular
terms paired so that they never occur together, holds 2.2 MB at a time
rather than 37 MB, and 200 such pages take 0.61 GB of resident memory
rather than 13.9 GB. Alone and warm it takes 96 ms instead of 82 ms;
under load its throughput and total CPU are the same.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JCzMKKrK3NstGKvaxsHhL2
When a query has a split term, each stage's answer covers only that
stage, so Matches asks for every term again at the next stage, and the
cold reader read and decoded every non-split term each time. The reader
now keeps each non-split entry it decodes and hands later stages the
same bitmap. A cold reader serves one request, so what it keeps is
bounded by that request's terms.

Callers already treat lookup results as read-only, so the two tests
that pinned a fresh bitmap per lookup on one reader go. One test now
pins that two readers share nothing, and another that a later stage
gets the whole term the first stage decoded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wwsd8qTEGeSb7poFEYcgg9
@tamirms
tamirms force-pushed the tamirms/events-split-terms branch from 67dde93 to 1297fdc Compare November 4, 2026 10:07
@tamirms
tamirms removed this pull request from stack #1157 November 4, 2026 10:20
@tamirms
tamirms added this pull request to stack #1184 November 4, 2026 10:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

1 participant