Repository navigation
Conversation
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
from
October 5, 2026 16:19
b7a055b to
6ab0f8d
Compare
tamirms
changed the base branch from
tamirms/packfile-index-directory
to
tamirms/events-windowed-matches
October 5, 2026 16:24
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
from
October 8, 2026 08:18
6ab0f8d to
51104ee
Compare
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
3 times, most recently
from
October 9, 2026 06:02
ce2cbc5 to
fb5d3d5
Compare
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
2 times, most recently
from
October 15, 2026 19:15
f5fec6d to
45d0638
Compare
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
from
October 19, 2026 13:00
45d0638 to
3f63c70
Compare
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
2 times, most recently
from
October 21, 2026 09:12
706417e to
32c9ac4
Compare
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
3 times, most recently
from
October 21, 2026 11:16
90ca6e0 to
7a96c85
Compare
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
2 times, most recently
from
October 21, 2026 14:07
165bf23 to
a0cd30d
Compare
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
from
October 21, 2026 14:43
a0cd30d to
faa318e
Compare
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
from
October 21, 2026 17:05
faa318e to
04e3395
Compare
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
from
October 21, 2026 17:15
04e3395 to
1f4b96e
Compare
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
7 times, most recently
from
October 24, 2026 02:49
58d0466 to
7d4ee1b
Compare
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
from
October 28, 2026 08:05
7d4ee1b to
f12aca8
Compare
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
from
November 4, 2026 10:07
f12aca8 to
0277985
Compare
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
from
November 4, 2026 19:03
0277985 to
884b080
Compare
A record closed only on its item count, so a few large items made it large, and a read of any item in it reads all of it. index.pack's large bitmaps share 128-item records with small ones, so a lookup of a rare term reads whatever its neighbors weigh, and events.pack's records grow when other contracts emit big events. index.pack's planned byte-only budget needs records limited by bytes alone. WriterOptions.MaxRecordBytes closes a record before an item that would take its raw bytes past the limit, so only a record of one item can exceed it. ItemsPerRecord 0 now means no item limit and needs MaxRecordBytes. The default of 128 for an unset ItemsPerRecord goes, and Create rejects options with no limit, and a byte limit with one item per record. Every production writer already sets ItemsPerRecord. A record closed early holds fewer items than the limit, so its index group gets a count column, a FOR group of its records' item counts marked by the directory's flag bit, and the directory's first items become running totals. Every group has counts when there is no item limit, and a pack of one item per record never does. Open checks each group's flag and item span, and a read checks a group's counts when it decodes it. Lookups already search first items, so they need no change. A record keeps the shape the pack's limit sets, so one closed at one item still carries its item size index and CRC. A pack whose records all fill is byte-identical to before, and a reader without counts rejects a pack with them. The trailer check for an item limit above 0 goes with its test, as do Open's reading of 0 as 1 and the test helper writeTestPackfile, which read an unset ItemsPerRecord as 1. Over six interleaved rounds with the file in the page cache, BenchmarkReader stays within 2.4% of before and BenchmarkOpen within 2.3%. On the same records closed at 128 items by a byte limit under an item limit of 256, so every group has counts, a ReadItem costs 0.3 µs more for the count column's decode, and ReadItems and ReadRange under 1% more. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KKssDnVqUbxVZqpxPN3FpP
tamirms
force-pushed
the
tamirms/packfile-byte-limit
branch
from
November 4, 2026 19:12
884b080 to
7d9a433
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A record is the unit of a read: reading any item in it reads all of it. Records closed only on their item count, so a few large items made a record large and every read of a small neighbour paid for them. In index.pack a rare term's bitmap shares a 128-item record with popular terms' bitmaps of up to a megabyte, so a lookup of the rare term read whatever its neighbours weighed: the worst small-term read was 1,062 KB on a real pubnet chunk and 5,757 KB on one folded to seven times its density. In events.pack one contract's big events inflate the reads of other contracts' events. And index.pack's new layout (a later PR in this stack) needs records bounded by bytes alone, so that every index read is one small EBS I/O.
The design
Records get a byte limit,
WriterOptions.MaxRecordBytes, which closes a record before an item that would take its raw bytes past the limit; only a record holding a single item can exceed it. The item limit stays, because events.pack's 128 events come to about 32 KB raw, so its records keep closing on count and store no counts; a byte-only budget there would grow the index read per open by 55% on the largest chunk (1.25 MB instead of 0.82 MB).ItemsPerRecord0 now means no item limit and requires a byte limit.A record closed early holds fewer items than the limit, so the index has to say how many. A count column per group, a compressed list of the group's record counts marked by the directory's flag bit, costs 0.9 KB on a real pubnet chunk and 6.0 KB on the largest against 2.0 and 13.1 KB for a flat per-record list, and in the worst case of ten million 16 KiB events it is 206 KB decoded per open instead of 10 MB. Counts in every group would add 13 to 74 KB to every open, and a pack-wide "counts present" flag is redundant with the directory's per-group bit. The directory's first items become running totals, so lookups keep their binary search. Packs whose records all fill carry no counts and are byte-identical to before, and a reader without counts rejects a pack with them.
How it works
Createnow rejects options with no limit at all, and a byte limit with one item per record; every production writer already sets an item limit. Open checks each group's flag and item span, and a read checks a group's counts when it decodes it. A record closed early keeps the shape its pack's limit sets, so it still carries its item size index and CRC. The code that assumed a positive item limit is gone with the assumption.Measured
Over six interleaved rounds with the file in the page cache, BenchmarkReader stays within 2.4% of before and BenchmarkOpen within 2.3%. On the same records closed at 128 items by a byte limit under an item limit of 256, so that every group has counts, a ReadItem costs 0.3 µs more for the count column's decode, and ReadItems and ReadRange under 1% more. Bit-flip sweeps of 9,920 and 18,312 single-bit mutations over packs with counts found one gap, a last group that carried no counts when its predecessor did, which is fixed.
🤖 Generated with Claude Code