Streaming spool rewards, V2 stake index, epoch boundary optimizations#200
Merged
Streaming spool rewards, V2 stake index, epoch boundary optimizations#200
Conversation
883aa84 to
b448181
Compare
- Remove runtime manifest dependency — seed replay state from state file - Replace in-memory reward calculation with streaming spool architecture - Remove global stake cache; stream directly from AccountsDB via stake index - Stake index with appendvec FileId/Offset hints for sequential I/O - Merge stake history + epoch stakes boundary scans into single pass - Collapse rewards from 3 AccountsDB scan phases to 2 via points spool Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
b448181 to
b00dca8
Compare
smcio
approved these changes
Feb 5, 2026
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.
Streaming spool rewards, stake index update, epoch boundary optimizations
17 files changed, +1835 / -940
This PR redesigns reward calculation and epoch boundary processing for significant performance improvements and memory savings. The core changes replace in-memory data structures with streaming I/O patterns and consolidate redundant AccountsDB scans.
The manifest seed migration (section 5) is the enabling prerequisite — by seeding all replay state into the state file at snapshot build time, the runtime no longer needs the manifest, which unlocks streaming directly from AccountsDB via the updated stake index.
1. Streaming Spool Reward Architecture
Files:
pkg/rewards/spool.go,pkg/rewards/rewards.go,pkg/rewards/points.goReplaces in-memory reward calculation with disk-backed streaming using temporary binary files ("spools"). Previously, all stake accounts and their computed rewards were held in memory simultaneously (~2GB+ on mainnet). Now, data flows through three sequential spool stages:
points = effective_stake × earned_creditsusing 128-bit math, and writes results to disk. A single-writer goroutine pattern with a 10k buffered channel handles concurrent stake processing.All spools use 1MB buffered readers/writers. Memory usage is now O(workers) instead of O(total_stakes).
2. Updated Stake Pubkey Index
Files:
pkg/accountsdb/index.go,pkg/global/global_ctx.goExtends the stake pubkey index from a flat list of 32-byte pubkeys to 48-byte records with appendvec location hints:
The index is sorted by
(FileId, Offset), enabling sequential appendvec reads during epoch boundary scans instead of random Pebble lookups. Hints are advisory — reads still go through Pebble for canonical location, so stale hints degrade gracefully to random I/O without correctness issues.This eliminates the ~500MB global stake cache. Stake data is now streamed directly from the sorted index via
StreamStakeAccounts.Compaction: Periodic compaction at epoch boundaries deduplicates appended entries (triggered when
entriesFlushedSinceCompact >= 1000), keeping the index file clean without rewriting on every boundary.3. Single-Pass Epoch Boundary Scan
Files:
pkg/replay/epoch.goConsolidates three separate AccountsDB scans into one streaming pass:
scanStakesForEpochBoundarythat accumulates all data in one pass, returning aBoundaryStakeScanResultwith atomic counters for stake history and mutex-guarded maps for epoch stakesThe downstream functions (
updateStakeHistorySysvar,updateEpochStakesAndRefreshVoteCache) now apply pre-computed results instead of scanning.4. Reward Phase Collapse (3 → 2)
Files:
pkg/rewards/rewards.go,pkg/replay/epoch.goThe points spool from Phase 1 eliminates the need to re-scan AccountsDB in Phase 2:
This also enables correct
numPartitionscalculation from actual reward count after filtering, instead of estimating from total stakes.5. Manifest Removal from Runtime
Files:
pkg/snapshot/manifest_seed.go,pkg/state/state.goReplay state is now fully self-contained in the state file. A new
PopulateManifestSeedfunction copies all necessary manifest data (capitalization, inflation, blockhashes, epoch stakes, etc.) into the state file at snapshot build time.Last*values instead of stale manifest dataManifestEpochStakescleared after first replayed slot to save memory🤖 Generated with Claude Code