Repository navigation
Conversation
tamirms
added this pull request to stack #1089
October 6, 2026 09:55
This was referenced Oct 6, 2026
tamirms
removed this pull request from stack #1089
October 8, 2026 08:18
tamirms
force-pushed
the
tamirms/hot-tier-window-index
branch
from
October 8, 2026 08:18
004654c to
1641df7
Compare
tamirms
added this pull request to stack #1102
October 8, 2026 08:19
tamirms
force-pushed
the
tamirms/hot-tier-window-index
branch
3 times, most recently
from
October 9, 2026 06:02
7264ea8 to
0588eeb
Compare
tamirms
force-pushed
the
tamirms/hot-tier-window-index
branch
from
October 15, 2026 19:15
0588eeb to
94c4c9a
Compare
…he hot tier
The tx-hash index of the window still being filled is rewritten at every
chunk boundary until the window terminates, so it is the one cold artifact
that is not write-once: on a throughput-capped volume the rewrite competes
with every cold read for as long as it runs. It now lives with the hot
tier: a coverage that is not terminal is written under the hot root, in a
txhash-index-{window} directory beside the chunk DBs, and the terminal
coverage is a cold artifact under the txhash index root, decided from the
coverage alone in Layout.TxHashIndexDir. The build, the sweep and the
reader all resolve the path from the coverage, so the terminal build lands
in the cold root and the same commit batch demotes the last hot-tier
coverage for the sweep. The layout now owns the window arithmetic, which
the catalog takes from it, so the two cannot disagree.
The events index build's spilled runs move from the index root to a
scratch directory under the hot root, so the cold roots hold only
artifacts and moving the hot root moves every build's transient writes
with it. The sample config names what belongs on the local disk (hot,
txhash_raw, captive core) and that the catalog stays on the durable
volume.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
tamirms
force-pushed
the
tamirms/hot-tier-window-index
branch
from
October 19, 2026 13:00
94c4c9a to
6d34f51
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.
Motivation
rpc v2 is meant to run with its data split across storage tiers: a small local NVMe disk for what is recent or rewritten, and a large durable volume (EBS today, possibly object storage later) for everything else. The
[storage]section already lets each store sit on its own disk. What makes the split work is that the cold stores are write-once: a chunk's ledger pack, events pack, events index and terminal tx-hash index are each written once, at freeze, and only read after that. The slow volume sees its reads plus one sequential write per chunk, and its throughput goes to serving queries.Two things did not fit that model.
The tx-hash index of the window still being filled. A window is 1000 chunks, and its index is rebuilt from scratch at every chunk boundary until the window is complete: the rebuild reads every
.bininput of the window and writes a new.idxthat grows with the window, then the previous.idxis swept. That is a sequential rewrite landing on the cold volume at every boundary, for the life of every window. On EBS the IOPS and bandwidth quotas are shared between reads and writes, so for as long as the rewrite runs, getTransaction and getEvents cold reads queue behind it. The gp3 baseline is 125 MiB/s and 3,000 IOPS, and a sequential rewrite of a growing index takes the bandwidth quota for as long as it runs.The events index build's spilled runs. Since #1098 the build keeps one slab of postings in memory and spills the rest as sorted runs on disk, reading them back three times at finalize. Those runs were written beside the index files, so every build, including the parallel backfill builds, put its transient I/O on the cold volume.
What changes
Both now live with the hot tier, which is where everything else that is rewritten already lives.
Layout.TxHashIndexDirtakes the coverage, not the window id. A coverage that is not terminal is written under the hot root, intxhash-index-{window}/beside the chunk DBs. A terminal coverage is a cold artifact undertxhash_index/{window}/. The build, the sweep and the reader all resolve the path from the coverage, so the terminal build lands in the cold root and the same commit batch demotes the last hot-tier coverage, which the sweep then removes from under hot. No catalog key or value changes: whether a coverage is terminal was already derived from the key.Layoutowns the tx-hash window arithmetic (WithTxHashIndex,TxHashIndex).catalog.Openreads it from the layout instead of taking a second parameter, so the two cannot disagree.{hot}/scratch/{chunk}-index.runs/instead of the index root. The cold roots hold only artifacts, and movinghotmoves every build's transient writes with it; there is no second key to forget.A two-tier deployment
The rule behind the split is write pattern, not age.
hottakes every ledger's writes (the chunk DBs), the per-boundary rewrite of the window index, and the builds' scratch.txhash_rawholds the.binfiles the per-boundary rebuild reads in full each time; they are written once per chunk but read at every boundary until the window terminates. Captive core churns its own database. Everything on the NVMe disk is derived data: the chunk DBs from the ledger backend, the window index and the.binfiles from the ledger packs. The catalog is the one thing nothing can rebuild from the files, so it stays on the durable volume with the artifacts it describes. The remaining stores default underdefault_data_dir.events_indexcan also go on the NVMe disk when filtered event queries over wide ranges matter more than the space, since the indexes are small and probed at random.🤖 Generated with Claude Code