#24230 indexes: Stop using node internal types and locking cs_main, improve sync logic

full analysis

https://github.com/bitcoin/bitcoin/pull/24230 · ryanofsky · +1138/-577 in 42 files, 18 commits · labels: UTXO Db and Indexes, Needs rebase · draft

Goal

  • Prevent lock contention by stopping indexes from holding the main lock during sync and startup
  • Decouple optional indexes from node internal types so they can eventually run in separate processes

Refactors optional indexes (txindex, blockfilterindex, coinstatsindex, txospenderindex) to remove dependencies on node internal types such as CBlockIndex, CChain, and CChainState. Moves chain synchronization logic out of BaseIndex into node::SyncChain and avoids holding cs_main during index initialization and notification handling. Extends interfaces::Chain and interfaces::Handler to support running indexes in separate processes and removes unused BlockInfo definitions from libbitcoinkernel.

Problem: Optional indexes currently depend directly on internal node data structures and hold cs_main during initial synchronization, which causes lock contention and prevents indexes from being run in separate processes or decoupled cleanly from node internals.

Category: Indexes (#4 of 7)

P2 · unblocks #10102

  • P2 because it eliminates main lock contention during index synchronization and startup
  • Decouples optional indexes from internal validation types to unblock running them out of process

P2 because it decouples the indexing framework from internal validation types (removing CBlockIndex pointers) and stops holding cs_main during index sync and initialization. The category guidelines highlight that refactors count when they remove a shared-lock dependency or decouple the index framework from node internals, which directly unblocks running indexes out of process (#10102).

Membership: Extensively refactors BaseIndex and all derived indexes (txindex, blockfilterindex, coinstatsindex, txospenderindex) and their sync logic.

Factors: security/stability 1, bug 1, performance 1, user value 1, leverage 2

Category: IPC / multiprocess (#14 of 20)

P3 · unblocks #10102

  • P3 because it builds interface abstractions needed for process separation of the index subsystem
  • Lays required interface groundwork without yet implementing the multiprocess transport layer

P3 because it builds out the interfaces abstraction layer necessary for process separation of the index subsystem (#10102). It does not implement the multiprocess transport or Cap'n Proto layer for indexes itself, but provides required interface support.

Membership: Extends interfaces::Chain and interfaces::Handler with connection and synchronization primitives specifically to allow indexes to run in separate processes.

Factors: security/stability 0, bug 0, performance 0, user value 0, leverage 2

Category: Kernel (libbitcoinkernel) (#17 of 18)

P4 · cleanup

  • P4 because it only performs a minor boundary cleanup by moving non-kernel types out of the kernel

P4 because it removes non-kernel types (BlockInfo) mistakenly placed in src/kernel back to interfaces and node, which is a minor boundary cleanup.

Membership: Modifies src/kernel/chain.cpp and src/kernel/chain.h to remove BlockInfo and MakeBlockInfo from the kernel library.

Factors: security/stability 0, bug 0, performance 0, user value 0, leverage 1

Reviewability: Stale: Needs rebase

  • Needs rebase due to merge conflicts with current master

The PR has merge conflicts with current master (mergeable_state dirty) and carries the 'Needs rebase' label.

Author status: active, rebasing and maintaining PR across upstream changes

Resolved concerns:

  • mzumsande discovered a crash when invalidateblock was called during an index sync; author fixed notification handling to ignore tip notifications during sync.
  • furszy identified a potential race condition between index sync and the validation queue when handling reorgs at startup; author resolved it by distinguishing sync state from notification readiness.
  • furszy identified swallowed return values in coinstatsindex; resolved via separate follow-up PR #28427.
  • Discussion around PR size and difficulty reviewing intermediate commits led author to simplify commits and document behavior.

Agreement: Strong

  • Broad concept and approach support across multiple maintainers with all code concerns resolved
  • Approach support for separating internal types toward multiprocess architecture (sedited)
  • Concept support verified through detailed testing across commits (mzumsande, josibake)
  • Concept approval without stated reasons (fjahr, aureleoules)
  • Resolved race conditions and crash on block invalidation during sync (mzumsande, furszy)

Strong concept and approach support across multiple maintainers with all code review concerns resolved

Multiple reviewers have given Concept and Approach ACKs. Detailed feedback on locking, reorg handling, and race conditions from mzumsande and furszy has been thoroughly addressed across iterations without any remaining objections.

  • sedited gave Approach ACK and reaffirmed support in 2026: 'I still think it would be a good idea.'
  • mzumsande and furszy provided extensive reviews and verified race condition fixes.

Objections:

ReviewerKindHarmStatusBlockingAuthor repliedQuote
mzumsandecorrectnessbitcoind crashed with leveldb assertion failure when invalidateblock was called during index syncresolvednoyes2022-02-15: 'If, at this commit, I am in the middle of syncing an index and at the same time call invalidateblock for the tip, my node coredumps'
Settled: 2022-02-18: 'Yes, I don't encounter this anymore now.'
furszycorrectnessrace condition where index background sync and validation queue could both process the same blocks concurrently on reorg during startupresolvednoyes2023-08-02: 'Which would enable the index new block connection signals reception at the same time that ThreadSync() is being executed.'
Settled: 2023-08-02: 'Updated ... to avoid race condition pointed out by furszy in an intermediate commit'
mzumsandecorrectnesssilent index corruption if sync process starts when CustomInit fails during startupresolvednoyes2022-06-23: "I think it would be good to abort early and not start the sync process if CustomInit of the initial blockConnected call failed (because the index might be corrupted)."
Settled: 2022-06-28: author added m_index.Interrupt() call on failure

Support:

  • sedited: Approach ACK: 'This change looks good, albeit its daunting length... I still think it would be a good idea.'
  • mzumsande: Concept ACK and detailed testing across commits
  • josibake: Concept ACK for multiprocess and kernel indexing improvements
  • fjahr: Concept ACK [not substantive]
  • aureleoules: Concept ACK [not substantive]

Participants: Sjors (support), fjahr (support), mzumsande (support), jonatack (neutral), maxim85200 (support), jamesob (support), maflcko (neutral), aureleoules (support), furszy (support), sedited (support), josibake (support)

State derived from the lists: substantive support, no open objection (sedited, mzumsande, josibake)

Review verdicts (DrahtBot): 0

Dependencies

Enables:

  • #10102 (Multiprocess bitcoin)
  • #15719 (Wallet passive startup)
  • #11756 (Multiwallet parallel rescan)

Files

283 lines under test/bench/ci.

  • src/index/base.cpp +305/-306
  • src/node/interfaces.cpp +182/-11
  • src/index/base.h +77/-52
  • src/node/chain.cpp +124/-0
  • src/test/baseindex_tests.cpp +61/-24
  • src/node/chain.h +67/-0
  • src/index/blockfilterindex.cpp +34/-25
  • src/test/util/index.cpp +39/-0
  • src/interfaces/types.h +35/-0
  • src/index/blockfilterindex.h +18/-15
  • src/test/util/index.h +32/-0
  • src/kernel/chain.h +0/-27
  • src/interfaces/chain.h +25/-0
  • src/test/blockfilter_index_tests.cpp +12/-12
  • test/functional/feature_init.py +22/-2
  • src/test/txindex_tests.cpp +12/-10
  • src/kernel/chain.cpp +0/-21
  • src/interfaces/handler.h +16/-3
  • src/index/txindex.cpp +10/-7
  • src/test/coinstatsindex_tests.cpp +7/-5
  • src/test/util/validation.h +0/-11
  • src/rpc/blockchain.cpp +5/-5
  • src/test/util/validation.cpp +0/-9
  • src/index/coinstatsindex.cpp +4/-4
  • src/index/txospenderindex.cpp +4/-4
  • src/net_processing.cpp +4/-4
  • src/index/txindex.h +6/-1
  • src/index/coinstatsindex.h +3/-3
  • src/index/txospenderindex.h +5/-1
  • src/test/txospenderindex_tests.cpp +4/-2
  • src/bench/index_blockfilter.cpp +4/-1
  • src/bench/wallet_create_tx.cpp +3/-2
  • src/util/btcsignals.h +5/-0
  • src/init.cpp +2/-2
  • src/rest.cpp +2/-2
  • test/functional/rpc_gettxspendingprevout.py +2/-2
  • test/functional/test_framework/test_node.py +2/-2
  • src/common/interfaces.cpp +2/-1
  • src/CMakeLists.txt +1/-0
  • src/rpc/node.cpp +1/-0
  • src/test/util/CMakeLists.txt +1/-0
  • src/validationinterface.h +0/-1

Card

Refactors optional indexes (txindex, blockfilterindex, coinstatsindex, txospenderindex) to stop using internal node types like CBlockIndex and eliminates holding cs_main during index sync and initialization. Sync logic is extracted from BaseIndex into node::SyncChain and consolidated with notification callbacks, resolving startup races and reducing lock contention. This architectural decoupling is a key prerequisite for running indexes in separate processes (#10102) and sharing sync logic with wallet rescans (#15719). The PR has strong conceptual support from multiple maintainers, but currently needs a rebase to resolve merge conflicts.

Data

dossier JSON · extract JSON · model openrouter/google/gemini-3.8-flash, generated 2026-09-17T21:15, confidence high, input hash 9fd3c010502494da