#32427 kernel: Replace leveldb-based BlockTreeDB with WAL and .dat file based store
https://github.com/bitcoin/bitcoin/pull/32427 · · +1757/-201 in 31 files, 10 commits · labels: UTXO Db and Indexes, Needs rebase · draft
Goal
- Allow external tools and indexers to read block data concurrently without stopping bitcoind
- Remove single-process locking constraints on block tree storage
This PR replaces the LevelDB-based block tree database (CBlockTreeDB) with a custom flat-file store (BlockTreeStore) using fixed-size serialized records, a write-ahead log (WAL) for atomic updates, and CRC32C checksums for corruption detection. It also provides automatic migration of existing LevelDB block indexes on startup and introduces directory-level file locking to guard against concurrent writer processes.
Problem: LevelDB acquires an exclusive file lock that prevents any other process from opening the database. External applications building on libbitcoinkernel (such as indexers like Electrs) cannot inspect block or header data concurrently while Bitcoin Core is running, forcing users to shut down the node first.
Category: Kernel (libbitcoinkernel) (#1 of 18)
P2 · new feature
- P2 because it unlocks concurrent read access for external tools using libbitcoinkernel
- Resolves a long-standing architectural obstacle preventing external tools from inspecting running nodes
P2 because LevelDB's exclusive file lock is a major architectural roadblock for libbitcoinkernel, preventing any external application from reading chain data concurrently with a running bitcoind instance. Resolving this unlocks major downstream integrations (e.g. electrs indexing).
Membership: Explicitly motivated by libbitcoinkernel multi-process support and adds kernel storage modules under src/kernel/.
Factors: security/stability 0, bug 0, performance 1, user value 2, leverage 2
Category: Validation (#13 of 48)
P2 · cleanup
- P2 because replacing the block index storage engine affects disk persistence across all full nodes
- Carries broad leverage by swapping core storage and adding an automated startup migration
P2 because replacing the block index storage mechanism changes critical on-disk state management, crash recovery, and LevelDB memory allocations in validation. While LevelDB works today for bitcoind, eliminating it for block headers reduces startup cache allocations and simplifies block index deserialization.
Membership: Replaces CBlockTreeDB in src/node/blockstorage.cpp and src/chain.h, changing how the node persists and loads CBlockIndex chain metadata.
Factors: security/stability 1, bug 0, performance 1, user value 1, leverage 2
Reviewability: Paused: Author reworking
- Hold off review because the author converted to draft to rework concurrent reading and WAL threading
- Branch also needs a rebase
The author converted the pull request to draft on 2026-07-09, stating they are working on true parallel reader access and mitigating the write synchronization penalty via asynchronous log application. The branch also has merge conflicts and needs a rebase.
Author status: converted to draft on 2026-07-09 to redesign parallel reader access and remove the write penalty; silent since
Open concerns:
- Directory-level syncing on Windows is a no-op, which may affect atomic flag persistence across crashes.
- The current WAL model applies changes immediately on the writer thread, which slows down FlushStateToDisk compared to LevelDB journaling and risks readers observing stale or contended files.
- Read-only mode in libbitcoinkernel is not yet wired to guarantee readers do not observe torn state if an unapplied WAL exists.
Resolved concerns:
- Added directory writer locking to fail fast if another writer attempts to access the store.
- Added timeouts to StoreLock polling to avoid indefinite hangs.
- Fixed memory usage during migration by using a local BlockMap rather than mutating node state.
- Added CRC32C checksums and magic/version headers across all index and log files.
Agreement: Mild
- Strong concept support for removing LevelDB locking constraints (theuni, josibake, stickies-v, marcofleon)
- Concerns about read-mode interactions with unapplied WAL files and lock ownership (willcl-ark)
- Technical feedback on WAL atomicity and platform-specific flush edge cases (stickies-v)
- Author converted to draft to redesign parallel readers and threaded WAL processing (sedited)
Strong concept support for moving away from LevelDB, but paused while author reworks WAL and reader concurrency to address feedback from willcl-ark and stickies-v
There is widespread agreement across kernel and validation contributors on the goal of decoupling block tree indexing from LevelDB exclusivity. Reviewers raised substantive concerns regarding WAL design and cross-platform atomic guarantees, prompting the author to put the PR in draft for architectural reworking.
- josibake: 'Waiting for Bitcoin Core to finish IBD... made this approach clunky. I'll revive this PoC as a means of testing this PR'
- marcofleon: 'I've differentially fuzzed BlockTreeDB and BlockTreeStore for ~5000 cpu hours so far and no issues.'
- willcl-ark: 'I still feel like the storage model is slightly in-between two WAL designs... Sticking with the current design then, are we happy with OpenMode::READ returning without checking for a pending committed WAL?'
- sedited: 'Going to convert this to draft for now... I also think that we should implement true parallel reader access without readers blocking each other, and I'd like to mitigate the write penalty...'
Objections:
| Reviewer | Kind | Harm | Status | Blocking | Author replied | Quote |
|---|---|---|---|---|---|---|
| stickies-v | correctness | Directory committing is a no-op on Windows, potentially breaking flag file atomicity on power loss | open | no | no | 2026-07-02: 'This is a no-op on Windows. I think this can lead to several edge case issues, like the block tree being marked as unpruned when blocks have already been removed, or the WAL not being applied atomically.' |
| willcl-ark | approach | READ mode readers could observe stale or incomplete data if a writer crashed leaving an unapplied WAL | open | no | yes | 2026-07-07: 'since READ-mode methods then read the data files directly, a reader could observe stale or partially applied data if log_flag.dat is present from a failed writer.' |
| ryanofsky | correctness | interrupted writes could corrupt data files without atomic updates or checksum detection | resolved | no | yes | 2025-05-07: 'How worried are we about file corruption here? I thought the main reason we use leveldb and sqlite databases in places like this where we don't need indexing is that they support atomic updates' Settled: 2025-06-10: 'The latest push updates the block tree store to use a write ahead log for atomic writes, and crc32c checksums to detect data corruption.' |
Support:
- josibake: Unblocks out-of-process index building in Electrs and Esplora via libbitcoinkernel without requiring Bitcoin Core to be stopped
- marcofleon: Differentially fuzzed BlockTreeDB vs BlockTreeStore for ~5000 CPU hours with no discrepancies
- theuni: Strong interest in removing the LevelDB dependency for block and header metadata
- w0xlt: The codebase changes seem surprisingly small and reducing the LevelDB dependency sounds good
- ismaelsadeeq: Excited about replacing LevelDB dependency in kernel [not substantive]
- stringintech: Supportive of the lock architecture improvements and contributed writer-lock testing
Participants: w0xlt (support), shahsb (question), josibake (support), Sjors (neutral), ryanofsky (objection), mzumsande (neutral), l0rinc (neutral), theuni (support), sipa (neutral), hodlinator (neutral), davidgumberg (question), maflcko (neutral), ismaelsadeeq (support), marcofleon (support), stickies-v (objection), HowHsu (support), craigraw (support), willcl-ark (objection), janb84 (support), alexanderwiederin (support), yuvicc (support), edilmedeiros (support), stringintech (support)
State derived from the lists: nonblocking objection open (stickies-v, willcl-ark) (model's own read: Positive w/ caveats)
Review verdicts (DrahtBot): 0 (+6)
- Stale ACK: willcl-ark, w0xlt, josibake, janb84, alexanderwiederin, yuvicc
- Approach ACK: edilmedeiros
- Concept ACK: theuni, ismaelsadeeq, marcofleon, l0rinc, stickies-v, HowHsu, craigraw
Dependencies
Enables:
- Parallel block data reading for external libbitcoinkernel consumers
Files
824 lines under test/bench/ci.
- src/kernel/blocktreestorage.cpp +622/-0
- src/test/blocktreestorage_tests.cpp +497/-0
- src/kernel/blocktreestorage.h +209/-0
- src/node/blockstorage.cpp +110/-47
- test/functional/feature_blocktree_migration.py +105/-0
- src/bench/write_block_index.cpp +99/-0
- src/node/blockstorage.h +5/-46
- src/test/fuzz/block_index.cpp +16/-16
- src/chain.h +25/-0
- test/functional/tool_bitcoin_chainstate.py +25/-0
- src/kernel/bitcoinkernel.cpp +5/-16
- src/test/kernel/test_kernel.cpp +8/-13
- src/init.cpp +5/-11
- test/functional/feature_init.py +8/-3
- src/kernel/bitcoinkernel.h +0/-10
- src/test/blockmanager_tests.cpp +2/-8
- src/test/util/setup_common.cpp +2/-7
- src/kernel/caches.h +1/-6
- src/test/validation_chainstatemanager_tests.cpp +1/-6
- src/kernel/bitcoinkernel_wrapper.h +0/-5
- src/kernel/blockmanager_opts.h +2/-1
- test/functional/feature_coinstatsindex_compatibility.py +3/-0
- doc/files.md +1/-1
- src/node/blockmanager_args.cpp +0/-2
- src/test/util/setup_common.h +0/-2
- src/validation.cpp +1/-1
- src/CMakeLists.txt +1/-0
- src/bench/CMakeLists.txt +1/-0
- src/kernel/CMakeLists.txt +1/-0
- src/test/CMakeLists.txt +1/-0
- test/functional/test_runner.py +1/-0
Card
PR #32427 replaces the LevelDB-based block tree database with a flat-file store backed by a write-ahead log and CRC32C checksums. The change solves a major limitation in libbitcoinkernel where LevelDB's exclusive process lock prevents secondary tools (like Electrs) from reading block and header indexes while bitcoind is running. Concept support is broad, but the PR is currently paused in draft while the author addresses WAL synchronization overhead and true parallel reader access raised by willcl-ark and stickies-v.