wallet: batch per-block writes in a single SQLite transaction #35237

pull IsmaelBuilds wants to merge 1 commits into bitcoin:master from IsmaelBuilds:wallet-batch-blockconnected-33618 changing 2 files +65 −23
  1. IsmaelBuilds commented at 10:47 AM on May 7, 2026: none

    When a connected block updates the wallet (a transaction relevant to the wallet is observed, or the periodic 144-block sync triggers), each individual write currently runs in its own implicit SQLite autocommit transaction. That forces an fsync per WriteKey, which is the proximate cause of the v29 -> v30 regression reported in #33618: in generatetoaddress-style workloads where every coinbase pays the wallet, each block goes from "no on-disk write" (pre-7bacabb204) to "two or three fsynced writes" (post-7bacabb204), making the RPC ~1.5-2x slower.

    Reverting 7bacabb204 is not an option: it fixes a correctness bug (#31824 — best-block locator was lagging across unclean shutdown during a reorg, leaving wallet transactions stuck as abandoned).

    This change keeps the correctness fix and addresses the perf regression by wrapping the per-block writes in a single explicit SQLite transaction opened at the top of CWallet::blockConnected and committed once at the end. All writes performed for the block (the new/updated wallet transactions, the best-block locator, conflict descendants, and address book updates for newly-seen receiving addresses) share the same WalletBatch, so SQLite issues one fsync at COMMIT time instead of one per individual key write.

    To make this safe, the same WalletBatch* is propagated through the relevant call paths:

    • SyncTransaction
    • AddToWalletIfInvolvingMe
    • AddToWallet
    • MarkConflicted (-> RecursiveUpdateTxState's batch overload)
    • WriteBestBlock
    • SetAddressBook (rerouted to SetAddressBookWithDB when a batch is active, since reusing a fresh WalletBatch on the same connection would block on m_write_semaphore that the active txn already holds)

    All new parameters default to nullptr, preserving call-site behavior for every other caller (rescan, mempool path, RPC, tests, etc.). Only the blockConnected path is batched.

    If TxnBegin() fails for any reason, the code transparently falls back to the previous unbatched behavior — correctness is unchanged. If TxnCommit() fails, the in-memory tip is preserved (it was already updated) and the on-disk best-block locator may briefly lag; the periodic 144-block sync and chainStateFlushed callback will reconcile on the next opportunity, mirroring how the wallet already tolerated crash-driven divergence prior to 7bacabb204.

    Fixes #33618.

    <!-- *** Please remove the following help text before submitting: *** Pull requests without a rationale and clear improvement may be closed immediately. GUI-related pull requests should be opened against https://github.com/bitcoin-core/gui first. See CONTRIBUTING.md -->

    <!-- Please provide clear motivation for your patch and explain how it improves Bitcoin Core user experience or Bitcoin Core developer experience significantly: * Any test improvements or new tests that improve coverage are always welcome. * All other changes should have accompanying unit tests (see `src/test/`) or functional tests (see `test/`). Contributors should note which tests cover modified code. If no tests exist for a region of modified code, new tests should accompany the change. * Bug fixes are most welcome when they come with steps to reproduce or an explanation of the potential issue as well as reasoning for the way the bug was fixed. * Features are welcome, but might be rejected due to design or scope issues. If a feature is based on a lot of dependencies, contributors should first consider building the system outside of Bitcoin Core, if possible. * Refactoring changes are only accepted if they are required for a feature or bug fix or otherwise improve developer experience significantly. For example, most "code style" refactoring changes require a thorough explanation why they are useful, what downsides they have and why they *significantly* improve developer experience or avoid serious programming bugs. Note that code style is often a subjective matter. Unless they are explicitly mentioned to be preferred in the [developer notes](/doc/developer-notes.md), stylistic code changes are usually rejected. -->

    <!-- Bitcoin Core has a thorough review process and even the most trivial change needs to pass a lot of eyes and requires non-zero or even substantial time effort to review. There is a huge lack of active reviewers on the project, so patches often sit for a long time. -->

  2. wallet: batch per-block writes in a single SQLite transaction
    When a connected block updates the wallet (a transaction relevant to the
    wallet is observed, or the periodic 144-block sync triggers), each
    individual write currently runs in its own implicit SQLite autocommit
    transaction. That forces an fsync per WriteKey, which is the proximate
    cause of the v29 -> v30 regression reported in #33618: in
    `generatetoaddress`-style workloads where every coinbase pays the wallet,
    each block goes from "no on-disk write" (pre-7bacabb204) to "two or three
    fsynced writes" (post-7bacabb204), making the RPC ~1.5-2x slower.
    
    Reverting 7bacabb204 is not an option: it fixes a correctness bug
    (#31824 — best-block locator was lagging across unclean shutdown during
    a reorg, leaving wallet transactions stuck as abandoned).
    
    This change keeps the correctness fix and addresses the perf regression
    by wrapping the per-block writes in a single explicit SQLite transaction
    opened at the top of `CWallet::blockConnected` and committed once at the
    end. All writes performed for the block (the new/updated wallet
    transactions, the best-block locator, conflict descendants, and address
    book updates for newly-seen receiving addresses) share the same
    `WalletBatch`, so SQLite issues one fsync at COMMIT time instead of one
    per individual key write.
    
    To make this safe, the same `WalletBatch*` is propagated through the
    relevant call paths:
    
      - SyncTransaction
      - AddToWalletIfInvolvingMe
      - AddToWallet
      - MarkConflicted (-> RecursiveUpdateTxState's batch overload)
      - WriteBestBlock
      - SetAddressBook (rerouted to SetAddressBookWithDB when a batch is
        active, since reusing a fresh WalletBatch on the same connection
        would block on m_write_semaphore that the active txn already holds)
    
    All new parameters default to nullptr, preserving call-site behavior for
    every other caller (rescan, mempool path, RPC, tests, etc.). Only the
    blockConnected path is batched.
    
    If TxnBegin() fails for any reason, the code transparently falls back to
    the previous unbatched behavior — correctness is unchanged. If
    TxnCommit() fails, the in-memory tip is preserved (it was already
    updated) and the on-disk best-block locator may briefly lag; the
    periodic 144-block sync and chainStateFlushed callback will reconcile on
    the next opportunity, mirroring how the wallet already tolerated
    crash-driven divergence prior to 7bacabb204.
    
    Fixes #33618.
    45cbd7bfc6
  3. DrahtBot added the label Wallet on May 7, 2026
  4. DrahtBot commented at 10:47 AM on May 7, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/35237.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process. A summary of reviews will appear here.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  5. achow101 commented at 11:40 AM on May 7, 2026: member

    I'm going to close this PR as the referenced issue does not describe anything that we want to change.

  6. achow101 closed this on May 7, 2026


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-05-11 12:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me