From a2b1c86903ab37000c145817b8d226fb218b663f Mon Sep 17 00:00:00 2001 From: Andrew Toth Date: Fri, 19 Jun 2026 10:25:09 -0400 Subject: [PATCH 1/2] txospenderindex: disable bloom filters to optimize disk usage LevelDB bloom filters are only consulted on Get point reads. They are not consulted for iterator seeks. txospenderindex only reads via iterator seeks, so building them is wasted effort and space. --- src/dbwrapper.cpp | 6 +++--- src/dbwrapper.h | 2 ++ src/index/base.cpp | 3 ++- src/index/base.h | 2 +- src/index/txospenderindex.cpp | 2 +- src/test/fuzz/dbwrapper.cpp | 1 + 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index ffe6f267a6b..ed914273634 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -136,12 +136,12 @@ static void SetMaxOpenFiles(leveldb::Options *options) { options->max_open_files, default_open_files); } -static leveldb::Options GetOptions(size_t nCacheSize) +static leveldb::Options GetOptions(size_t nCacheSize, bool bloom_filter) { leveldb::Options options; options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously - options.filter_policy = leveldb::NewBloomFilterPolicy(10); + options.filter_policy = bloom_filter ? leveldb::NewBloomFilterPolicy(10) : nullptr; options.compression = leveldb::kNoCompression; options.info_log = new CBitcoinLevelDBLogger(); if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) { @@ -225,7 +225,7 @@ CDBWrapper::CDBWrapper(const DBParams& params) DBContext().iteroptions.verify_checksums = true; DBContext().iteroptions.fill_cache = false; DBContext().syncoptions.sync = true; - DBContext().options = GetOptions(params.cache_bytes); + DBContext().options = GetOptions(params.cache_bytes, params.bloom_filter); DBContext().options.create_if_missing = true; DBContext().options.max_file_size = params.max_file_size; assert(!(params.testing_env && params.memory_only)); diff --git a/src/dbwrapper.h b/src/dbwrapper.h index 83da6febe75..1e2174e2f3d 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -47,6 +47,8 @@ struct DBParams { //! If true, store data obfuscated via simple XOR. If false, XOR with a //! zero'd byte array. bool obfuscate = false; + //! If true, build a LevelDB bloom filter to accelerate point lookups. + bool bloom_filter = true; //! Passed-through options. DBOptions options{}; //! If non-null, use this as the leveldb::Env instead of the default. diff --git a/src/index/base.cpp b/src/index/base.cpp index 906ed265174..3c474d7cc2f 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -65,13 +65,14 @@ CBlockLocator GetLocator(interfaces::Chain& chain, const uint256& block_hash) return locator; } -BaseIndex::DB::DB(const fs::path& path, size_t n_cache_size, bool f_memory, bool f_wipe, bool f_obfuscate) : +BaseIndex::DB::DB(const fs::path& path, size_t n_cache_size, bool f_memory, bool f_wipe, bool f_obfuscate, bool f_bloom) : CDBWrapper{DBParams{ .path = path, .cache_bytes = n_cache_size, .memory_only = f_memory, .wipe_data = f_wipe, .obfuscate = f_obfuscate, + .bloom_filter = f_bloom, .options = [] { DBOptions options; node::ReadDatabaseArgs(gArgs, options); return options; }()}} {} diff --git a/src/index/base.h b/src/index/base.h index 6d7e86ec0fa..c39ac7a365c 100644 --- a/src/index/base.h +++ b/src/index/base.h @@ -65,7 +65,7 @@ protected: { public: DB(const fs::path& path, size_t n_cache_size, - bool f_memory = false, bool f_wipe = false, bool f_obfuscate = false); + bool f_memory = false, bool f_wipe = false, bool f_obfuscate = false, bool f_bloom = true); /// Read block locator of the chain that the index is in sync with. /// Note, the returned locator will be empty if no record exists. diff --git a/src/index/txospenderindex.cpp b/src/index/txospenderindex.cpp index 50b9bfeb8b5..304104df02e 100644 --- a/src/index/txospenderindex.cpp +++ b/src/index/txospenderindex.cpp @@ -62,7 +62,7 @@ struct DBKey { }; TxoSpenderIndex::TxoSpenderIndex(std::unique_ptr chain, size_t n_cache_size, bool f_memory, bool f_wipe) - : BaseIndex(std::move(chain), "txospenderindex", "txospenderidx"), m_db{std::make_unique(gArgs.GetDataDirNet() / "indexes" / "txospenderindex" / "db", n_cache_size, f_memory, f_wipe)} + : BaseIndex(std::move(chain), "txospenderindex", "txospenderidx"), m_db{std::make_unique(gArgs.GetDataDirNet() / "indexes" / "txospenderindex" / "db", n_cache_size, f_memory, f_wipe, /*f_obfuscate=*/false, /*f_bloom=*/false)} { if (!m_db->Read("siphash_key", m_siphash_key)) { FastRandomContext rng(false); diff --git a/src/test/fuzz/dbwrapper.cpp b/src/test/fuzz/dbwrapper.cpp index 265f0ca3a09..66b7327f64a 100644 --- a/src/test/fuzz/dbwrapper.cpp +++ b/src/test/fuzz/dbwrapper.cpp @@ -188,6 +188,7 @@ DBParams ConsumeDBParams(FuzzedDataProvider& provider, leveldb::Env* testing_env .path = "dbwrapper_fuzz", .cache_bytes = provider.ConsumeIntegralInRange(64 << 10, 1_MiB), .obfuscate = obfuscate, + .bloom_filter = provider.ConsumeBool(), .options = options, .testing_env = testing_env, .max_file_size = provider.ConsumeBool() From 6d0ea4cf5bdc97331d4550a35d361a7d13d259a4 Mon Sep 17 00:00:00 2001 From: Andrew Toth Date: Sat, 4 Jul 2026 11:30:38 -0400 Subject: [PATCH 2/2] doc: add release notes --- doc/release-notes-35634.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/release-notes-35634.md b/doc/release-notes-35634.md index ccbd7fd7c22..2398305bdf0 100644 --- a/doc/release-notes-35634.md +++ b/doc/release-notes-35634.md @@ -1,9 +1,8 @@ Indexes ------- -- The transaction output spender index (`-txospenderindex`) now stores one byte - less per spender entry for newly indexed blocks. Existing indexes remain - compatible and no action is required. To apply the same saving to entries - indexed before upgrading, stop the node, delete the +- The transaction output spender index (`-txospenderindex`) now uses less disk + space. Existing indexes remain compatible and no action is required. To reclaim + the space for data indexed before upgrading, stop the node, delete the `/indexes/txospenderindex/` directory, and restart with - `-txospenderindex` enabled to rebuild it. + `-txospenderindex` enabled to rebuild it. (#35634, #35568)