Skip to content

Conversation

@patrick-ogrady
Copy link
Contributor

@patrick-ogrady patrick-ogrady commented Jan 6, 2026

Summary (from Claude)

Redesigns archive and freezer storage to split data across two backends:

  • Index journal: Fixed-size entries using buffer pool
  • Value storage (glob): Sequential append-only storage that bypasses the buffer pool

Key Changes

  • Add Manager abstraction to deduplicate blob lifecycle management across segmented journals
  • Add BufferFactory trait with AppendFactory (uses buffer pool) and WriteFactory (direct I/O)
  • Add Glob journal for sequential value storage without buffer pool overhead
  • Add Oversized abstraction combining fixed index journal + glob with automatic crash recovery
  • Migrate archive/prunable, archive/immutable, and freezer to new split architecture
  • Optimize journal append methods to pre-allocate buffers and avoid memory copies
  • Add comprehensive conformance tests for all journal types and archive storage

Crash Recovery

The Oversized module handles unclean shutdown by validating index entries against actual glob sizes:

  • Index entries pointing to non-existent glob data are detected and rewound
  • Orphan glob values without index entries are acceptable (cleaned up on next prune)
  • Prune operations are sequential (index first, glob second) for crash safety
  • Recovery is O(sections) by only checking the last entry per section

Config Changes

Archive and freezer configs now have separate partitions and write buffers for key/index and value storage:

  • key_partition / value_partition (was single partition)
  • key_write_buffer / value_write_buffer (was single write_buffer)
  • key_buffer_pool for index journal caching

patrick-ogrady and others added 4 commits January 5, 2026 13:41
Adds a benchmark for archive get operations with 64KB values to
better represent realistic block-sized workloads. The existing
32-byte value benchmark heavily favors buffer pool caching
(~500 values per page), which is not representative of actual
blockchain storage patterns.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Adds a benchmark that uses a tiny buffer pool (10 pages = 160KB)
to simulate realistic cache contention scenarios. This validates
that the split storage design performs better under cache pressure.

Results show spike is 3-4x faster than main for serial reads when
the cache is too small to hold the working set, confirming the
hypothesis that bypassing the buffer pool is beneficial when
cache hit rates are low.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@patrick-ogrady patrick-ogrady added this to the v0.1.0 milestone Jan 6, 2026
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Jan 6, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
commonware-mcp 5de9e25 Jan 08 2026, 01:29 AM

Extracts common blob management logic from fixed.rs and variable.rs into
a reusable BlobManager component. This eliminates ~340 lines of duplicate
code for init, sync, prune, destroy, and rewind operations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Jan 6, 2026

Deploying monorepo with  Cloudflare Pages  Cloudflare Pages

Latest commit: 5de9e25
Status:⚡️  Build in progress...

View logs

…journal replay

clone_blob() returns a Future that needs to be awaited, while clone()
directly clones the Append wrapper which is what we need for the stream.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
patrick-ogrady and others added 8 commits January 6, 2026 10:06
… module

- Rename blob_manager.rs to manager.rs and BlobManager to Manager
- Add BufferFactory trait with AppendFactory (pool caching) and WriteFactory (no caching)
- Make Manager generic over BufferFactory to support both buffer types
- Move archive/blob.rs to journal/segmented/glob.rs using Manager with WriteFactory
- Rename glob::Blob to glob::Glob
- Update freezer and prunable archive to use new glob module
- Each journal (fixed, variable, glob) now has its own Config struct
- Remove unused section verification code during replay
- Update documentation references

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Introduce a new `Oversized` module that combines `FixedJournal` + `Glob`
with automatic crash recovery. This ensures index entries always point
to valid glob data after an unclean shutdown.

Key changes:
- Add `OversizedEntry` trait for entries that reference oversized values
- Add `Oversized` struct with crash recovery validation during init
- Migrate `archive/prunable` to use `Oversized` instead of separate journals
- Migrate `freezer` to use `Oversized` instead of separate journals
- Add `rewind_section` method to `FixedJournal` for crash recovery
- Add `key_index_replay_buffer` config field to freezer

The crash recovery algorithm validates each index entry's value location
against the actual glob size, rewinding the index journal to exclude any
trailing invalid entries that reference non-existent glob data.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
…O(entries)

The previous recovery algorithm loaded ALL index entries into memory to
validate them against glob sizes. This was O(total entries) in both
memory and time.

The new algorithm only checks the last entry in each section. Since
entries are appended sequentially and value offsets are monotonically
increasing within a section, if the last entry is valid then all earlier
entries must be valid too.

Changes:
- Rewrite Oversized::recover() to only check last entry per section
- Add sections() method to fixed::Journal
- Remove replay_buffer from Oversized::Config (no longer needed)
- Remove key_index_replay_buffer from Freezer::Config
- Add 9 comprehensive recovery tests covering edge cases

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Expand Glob, Oversized, and related types to use u64 for value
  offset and size instead of u32, removing the ~4GB per-section limit
- Add conformance hash tests to fixed.rs and glob.rs to detect
  accidental format changes
- Fix Oversized::prune() to return bool for API consistency with
  Manager::prune()

Breaking change: on-disk format changed for KeyEntry and IndexEntry.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Add 16-byte alignment to glob storage, allowing u32 offsets to address
~64GB per section while saving 8 bytes per entry compared to u64.

Changes:
- Add shared `align` module with alignment utilities
- Update glob.rs to write entries at aligned positions
- Revert OversizedEntry trait to (u32, u32) for offset/size
- Revert Cursor from (u64, u64, u64) to (u64, u32, u32)
- Revert KeyEntry and IndexEntry to use u32 for value location

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Change prune() from parallel to sequential (index first, glob second)
  to ensure crash safety. If crash occurs after index prune but before
  glob prune, orphan data in glob is acceptable and cleaned up on next
  prune.

- Add explicit warning log when recover() detects glob has pruned a
  section that index still has (indicates crash during prune).

- Add three new recovery tests:
  - test_recovery_glob_pruned_but_index_not
  - test_recovery_index_partition_deleted
  - test_recovery_index_synced_but_glob_not

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copy link
Contributor Author

@patrick-ogrady patrick-ogrady left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

almost there!

danlaine
danlaine previously approved these changes Jan 7, 2026
@patrick-ogrady patrick-ogrady merged commit 8f7a0d7 into main Jan 8, 2026
10 of 12 checks passed
@patrick-ogrady patrick-ogrady deleted the patrick/improve-buffer-pool-usage branch January 8, 2026 01:29
@codecov
Copy link

codecov bot commented Jan 8, 2026

Codecov Report

❌ Patch coverage is 95.73132% with 68 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.14%. Comparing base (c16f312) to head (5de9e25).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
storage/src/journal/segmented/fixed.rs 95.90% 17 Missing ⚠️
runtime/src/storage/memory.rs 0.00% 11 Missing ⚠️
storage/src/freezer/storage.rs 93.02% 9 Missing ⚠️
storage/src/journal/segmented/glob.rs 96.32% 9 Missing ⚠️
storage/src/journal/segmented/manager.rs 96.80% 6 Missing ⚠️
storage/src/journal/segmented/variable.rs 98.29% 5 Missing ⚠️
runtime/src/deterministic.rs 0.00% 3 Missing ⚠️
runtime/src/storage/audited.rs 0.00% 3 Missing ⚠️
runtime/src/storage/metered.rs 0.00% 3 Missing ⚠️
storage/src/journal/contiguous/variable.rs 85.71% 2 Missing ⚠️
@@            Coverage Diff             @@
##             main    #2707      +/-   ##
==========================================
+ Coverage   92.91%   93.14%   +0.22%     
==========================================
  Files         363      368       +5     
  Lines      108104   110795    +2691     
==========================================
+ Hits       100450   103196    +2746     
+ Misses       7654     7599      -55     
Files with missing lines Coverage Δ
consensus/src/marshal/actor.rs 92.88% <100.00%> (+0.01%) ⬆️
consensus/src/marshal/cache.rs 96.52% <100.00%> (+1.34%) ⬆️
consensus/src/marshal/mod.rs 95.28% <100.00%> (+0.04%) ⬆️
storage/src/archive/immutable/mod.rs 100.00% <100.00%> (ø)
storage/src/archive/immutable/storage.rs 91.05% <100.00%> (+0.09%) ⬆️
storage/src/archive/mod.rs 99.47% <100.00%> (+<0.01%) ⬆️
storage/src/archive/prunable/mod.rs 100.00% <100.00%> (ø)
storage/src/archive/prunable/storage.rs 94.36% <100.00%> (+0.74%) ⬆️
storage/src/cache/storage.rs 92.46% <ø> (ø)
storage/src/freezer/mod.rs 99.71% <100.00%> (+0.01%) ⬆️
... and 12 more

... and 12 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c16f312...5de9e25. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-format This PR modifies codec and/or storage formats.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants