wallet: batch all individual spkms setup db writes in a single db txn #28894

pull furszy wants to merge 5 commits into bitcoin:master from furszy:2023_wallet_batch_keypool_creation changing 8 files +111 −26
  1. furszy commented at 2:44 pm on November 16, 2023: member

    Work decoupled from #28574.

    Instead of performing multiple single write operations per spkm setup call, this PR batches them all within a single atomic db txn.

    Speeding up the process and preventing the wallet from entering an inconsistent state if any of the intermediate transactions fail (which shouldn’t happen but.. if it does, it is better to not store any spkm rather than storing them partially).

    To compare the changes, added benchmark in the first commit.

  2. bench: add benchmark for wallet creation procedure bb4554c81e
  3. DrahtBot commented at 2:44 pm on November 16, 2023: contributor

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

    Code Coverage

    For detailed information about the code coverage, see the test coverage report.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK Sjors, achow101, BrandonOdiwuor, theStack
    Approach ACK S3RK

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #28574 (wallet: optimize migration process, batch db transactions by furszy)
    • #28333 (wallet: Construct ScriptPubKeyMans with all data rather than loaded progressively by achow101)
    • #27865 (wallet: Track no-longer-spendable TXOs separately by achow101)
    • #27286 (wallet: Keep track of the wallet’s own transaction outputs in memory by achow101)
    • #26008 (wallet: cache IsMine scriptPubKeys to improve performance of descriptor wallets by achow101)
    • #25907 (wallet: rpc to add automatically generated descriptors by achow101)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  4. DrahtBot renamed this:
    wallet: batch all atomic spkms setup db writes in a single db txn
    wallet: batch all atomic spkms setup db writes in a single db txn
    on Nov 16, 2023
  5. DrahtBot added the label Wallet on Nov 16, 2023
  6. furszy renamed this:
    wallet: batch all atomic spkms setup db writes in a single db txn
    wallet: batch all individual spkms setup db writes in a single db txn
    on Nov 16, 2023
  7. DrahtBot renamed this:
    wallet: batch all individual spkms setup db writes in a single db txn
    wallet: batch all individual spkms setup db writes in a single db txn
    on Nov 16, 2023
  8. theStack commented at 3:57 pm on November 17, 2023: contributor

    Concept ACK

    Didn’t look deeper at the code yet, but ran the new benchmark (on an SSD) for comparison via

    0$ ./src/bench/bench_bitcoin -filter=WalletCreatePlain\|WalletCreateEncrypted
    

    master (commit bb4554c81e0d819d74996f89cbb9c00476aedf8c which adds the benchmark)

    ns/op op/s err% total benchmark
    797,011,189.00 1.25 0.6% 8.74 WalletCreateEncrypted
    391,984,469.00 2.55 1.1% 4.30 WalletCreatePlain

    PR head (1901360ca52c6563a40dc78633cc492a822ac7a9):

    ns/op op/s err% total benchmark
    724,523,571.00 1.38 0.5% 7.97 WalletCreateEncrypted
    319,389,684.00 3.13 0.8% 3.50 WalletCreatePlain

    Looks like a rough ~10% speedup for encrypted and ~22% speedup for plain wallets on my machine. Even without speedups, I think it’s good practice to avoid entering an inconsistent state, as mentioned in the PR description.

  9. in src/wallet/scriptpubkeyman.h:586 in da133a80ba outdated
    581@@ -582,6 +582,9 @@ class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
    582     // Fetch the SigningProvider for a given index and optionally include private keys. Called by the above functions.
    583     std::unique_ptr<FlatSigningProvider> GetSigningProvider(int32_t index, bool include_private = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
    584 
    585+    //! Same as 'TopUp' but designed for use within a batch transaction context
    586+    bool TopUpWithDb(WalletBatch& batch, unsigned int size = 0);
    


    Sjors commented at 10:04 am on November 21, 2023:
    da133a80bae311373337a0d9c474667a5f73908d nit: TopUpWithDB

    furszy commented at 2:03 am on November 22, 2023:
    Done as suggested
  10. in src/wallet/wallet.cpp:3560 in 1901360ca5 outdated
    3558-            AddActiveScriptPubKeyMan(id, t, internal);
    3559+            AddActiveScriptPubKeyManWithDb(batch, id, t, internal);
    3560         }
    3561     }
    3562+
    3563+    // Ensure information is dumped to disk
    


    Sjors commented at 10:16 am on November 21, 2023:
    1901360ca52c6563a40dc78633cc492a822ac7a9: nit “is committed to disk”

    furszy commented at 2:03 am on November 22, 2023:
    Done as suggested
  11. Sjors approved
  12. Sjors commented at 10:40 am on November 21, 2023: member

    ACK 1901360ca52c6563a40dc78633cc492a822ac7a9

    Not sure if b85b75d6470841813c5844b2e9c883360f1ad94a is needed since we’re getting rid of the legacy wallet anyway. But it looks correct.

    Ran bench on MacBook Pro 2019 (2,3 GHz 8-Core Intel Core i9, SSD):

    Before:

    0src/bench/bench_bitcoin -filter="WalletCreate[E|P].*" -min-time=30000                          
    
    ns/op op/s err% total benchmark
    924,961,364.67 1.08 1.8% 30.41 WalletCreateEncrypted
    537,964,679.50 1.86 2.3% 35.03 WalletCreatePlain

    After:

    ns/op op/s err% total benchmark
    777,090,017.50 1.29 1.3% 32.65 WalletCreateEncrypted
    361,932,639.25 2.76 0.6% 33.34 WalletCreatePlain

    Here’s a commit that batches the descriptor import for external signers: https://github.com/Sjors/bitcoin/commit/286883a68d24e3600d9e608ccdea55763c50dde1 (I had to make TopupWithDB() public)

  13. DrahtBot requested review from theStack on Nov 21, 2023
  14. wallet: batch descriptor spkm TopUp
    Instead of performing multiple atomic write
    operations per descriptor setup call, batch
    them all within a single atomic db txn.
    075aa44ceb
  15. wallet: batch legacy spkm TopUp
    Instead of performing multiple atomic write
    operations per legacy spkm setup call, batch
    them all within a single atomic db txn.
    3eb769f150
  16. wallet: descriptors setup, batch db operations
    Instead of doing one db transaction per descriptor setup,
    batch all descriptors' setup writes in a single db txn.
    
    Speeding up the process and preventing the wallet from entering
    an inconsistent state if any of the intermediate transactions
    fail.
    1f65241b73
  17. furszy force-pushed on Nov 22, 2023
  18. wallet: batch external signer descriptor import
    Co-authored-by: furszy <matiasfurszyfer@protonmail.com>
    f053024273
  19. furszy force-pushed on Nov 22, 2023
  20. furszy commented at 2:09 am on November 22, 2023: member

    Updated per feedback, thanks for the review @Sjors.

    Here’s a commit that batches the descriptor import for external signers: https://github.com/Sjors/bitcoin/commit/286883a68d24e3600d9e608ccdea55763c50dde1 (I had to make TopupWithDB() public)

    Thanks. Pulled the commit and made two changes to it:

    1. Instead of making TopupWithDB() public, made it protected. The ExternalSignerScriptPubKeyMan class is derived from DescriptorScriptPubKeyMan.
    2. Removed the unimplemented SetupDescriptor() function from DescriptorScriptPubKeyMan. Only ExternalSignerScriptPubKeyMan utilizes it.
  21. Sjors commented at 2:13 pm on November 22, 2023: member

    re-utACK f05302427386fe63f4929a7198652cb1e4ab3bcc

    CI failure seems spurious

  22. S3RK commented at 8:56 am on November 27, 2023: contributor
    Concept and Approach ACK
  23. achow101 commented at 9:56 pm on December 4, 2023: member
    ACK f05302427386fe63f4929a7198652cb1e4ab3bcc
  24. DrahtBot requested review from S3RK on Dec 4, 2023
  25. BrandonOdiwuor approved
  26. BrandonOdiwuor commented at 3:13 am on December 8, 2023: contributor
    ACK f05302427386fe63f4929a7198652cb1e4ab3bcc
  27. theStack approved
  28. theStack commented at 9:36 am on December 8, 2023: contributor
    Code-review ACK f05302427386fe63f4929a7198652cb1e4ab3bcc
  29. fanquake merged this on Dec 8, 2023
  30. fanquake closed this on Dec 8, 2023

  31. in src/bench/wallet_create.cpp:35 in f053024273
    30+
    31+    DatabaseStatus status;
    32+    bilingual_str error_string;
    33+    std::vector<bilingual_str> warnings;
    34+
    35+    fs::path wallet_path = test_setup->m_path_root / strprintf("test_wallet_%d", random.rand32()).c_str();
    


    maflcko commented at 12:19 pm on December 8, 2023:
    question: Does it need to be random? m_path_root should already be (more) random. If not, it would be good to remove. Also, to reduce the use of c_str, which (in other parts of the code) is fragile and can lead to bugs. So git grep c_str will be less verbose if unused used of c_str are removed.

    furszy commented at 2:04 pm on December 8, 2023:

    no, it doesn’t. Initially, the wallet_path was inside the bench.run() lambda to create wallets under different paths. This was to avoid flushing the wallet to disk and removing the directory within the benchmark execution (which has nothing to do with what we are trying to benchmark). Then, at the end, I changed the approach to occupy less memory.

    But.. thinking it further now, we could go back to the previous approach and not be concerned about memory by setting the max iterations number. Also, probably for another working path, wouldn’t be bad to introduce a way to clean up stuff within the benchmark execution that is deliberately not included in the bench results.

    Thoughts?


    maflcko commented at 3:49 pm on December 8, 2023:

    Also, probably for another working path, wouldn’t be bad to introduce a way to clean up stuff within the benchmark execution that is deliberately not included in the bench results.

    Sgtm, but if the cleanup only takes a very short time, I think it is fine to just leave as-is.

  32. in src/bench/wallet_create.cpp:37 in f053024273
    32+    bilingual_str error_string;
    33+    std::vector<bilingual_str> warnings;
    34+
    35+    fs::path wallet_path = test_setup->m_path_root / strprintf("test_wallet_%d", random.rand32()).c_str();
    36+    bench.run([&] {
    37+        auto wallet = CreateWallet(context, wallet_path.u8string(), /*load_on_start=*/std::nullopt, options, status, error_string, warnings);
    


    maflcko commented at 9:56 pm on December 14, 2023:
    nit: u8string looks wrong here, according to the fs.h documentation.
  33. achow101 referenced this in commit 6ff0aa089c on Feb 12, 2024
  34. achow101 referenced this in commit c16e909b3e on Oct 24, 2024
  35. bitcoin locked this on Dec 13, 2024

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: 2024-12-28 03:12 UTC

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