RocksDB+IBD integration: CActiveTxDB wrapper, dual-backend utxosnapshot, headers-first IBD patch, backend-aware bootstrap
Build All Platforms / test-linux-unit (push) Failing after 40s
Build All Platforms / build-linux-qt (push) Failing after 40s
Build All Platforms / build-linux-daemon (push) Failing after 40s
Build All Platforms / build-windows-qt (push) Has been cancelled
Build All Platforms / build-windows-daemon (push) Has been cancelled
Build All Platforms / build-macos (push) Has been cancelled
Build All Platforms / release (push) Has been cancelled
Build All Platforms / Trigger TRI-PI ARM64 Build (push) Has been cancelled
Build All Platforms / test-linux-unit (push) Failing after 40s
Build All Platforms / build-linux-qt (push) Failing after 40s
Build All Platforms / build-linux-daemon (push) Failing after 40s
Build All Platforms / build-windows-qt (push) Has been cancelled
Build All Platforms / build-windows-daemon (push) Has been cancelled
Build All Platforms / build-macos (push) Has been cancelled
Build All Platforms / release (push) Has been cancelled
Build All Platforms / Trigger TRI-PI ARM64 Build (push) Has been cancelled
This commit is contained in:
@@ -74,6 +74,7 @@ set(CORE_SOURCES
|
||||
rpcsmessage.cpp
|
||||
zmqpublishnotifier.cpp
|
||||
txdb-base.cpp
|
||||
txdb.cpp
|
||||
txdb-leveldb.cpp
|
||||
utxosnapshot.cpp
|
||||
snapshotnet.cpp
|
||||
|
||||
+9
-8
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "bootstrap.h"
|
||||
#include "utxosnapshot.h"
|
||||
#include "txdb.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
@@ -718,16 +719,16 @@ bool DownloadBootstrap(const std::string& host,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the archive included a trusted pre-built index (txleveldb/)
|
||||
// with a valid snapshot.manifest. If verified, keep it to skip the
|
||||
// Check if the archive included a trusted pre-built index for the active
|
||||
// backend with a valid snapshot.manifest. If verified, keep it to skip the
|
||||
// multi-hour FastImportBlockFile() rebuild.
|
||||
fs::path txleveldb = dataDir / "txleveldb";
|
||||
fs::path chainDbPath = dataDir / GetActiveChainDbDirName();
|
||||
fs::path database = dataDir / "database";
|
||||
fs::path manifestPath = dataDir / "snapshot.manifest";
|
||||
|
||||
bool keepIndex = false;
|
||||
|
||||
if (fs::exists(manifestPath) && fs::exists(txleveldb)) {
|
||||
if (fs::exists(manifestPath) && fs::exists(chainDbPath)) {
|
||||
SnapshotManifest manifest;
|
||||
std::string manifestError;
|
||||
|
||||
@@ -754,9 +755,9 @@ bool DownloadBootstrap(const std::string& host,
|
||||
if (!keepIndex) {
|
||||
// No valid manifest or verification failed - delete the index.
|
||||
// FastImportBlockFile() will rebuild from blk0001.dat on next startup.
|
||||
printf("Bootstrap: removing extracted txleveldb/ (will rebuild index from blk0001.dat)\n");
|
||||
if (fs::exists(txleveldb))
|
||||
fs::remove_all(txleveldb);
|
||||
printf("Bootstrap: removing extracted %s/ (will rebuild index from blk0001.dat)\n", GetActiveChainDbDirName());
|
||||
if (fs::exists(chainDbPath))
|
||||
fs::remove_all(chainDbPath);
|
||||
}
|
||||
|
||||
// Always remove BDB database/ dir (wallet environment from another machine)
|
||||
@@ -791,7 +792,7 @@ bool DownloadUtxoSnapshot(const std::string& host,
|
||||
|
||||
printf("Bootstrap: UTXO snapshot downloaded, loading into database...\n");
|
||||
|
||||
// Load the snapshot into a fresh txleveldb
|
||||
// Load the snapshot into a fresh active chain DB
|
||||
if (!UtxoSnapshot::LoadSnapshot(tmpPath, dataDir, strError)) {
|
||||
fs::remove(tmpPath);
|
||||
return false;
|
||||
|
||||
+10
-10
@@ -250,7 +250,7 @@ void Shutdown(void* parg)
|
||||
pNotificationQueue = NULL;
|
||||
}
|
||||
|
||||
// CTxDB().Close();
|
||||
// CActiveTxDB().Close();
|
||||
bitdb.Flush(false);
|
||||
bitdb.Flush(true);
|
||||
fs::remove(GetPidFile());
|
||||
@@ -955,7 +955,7 @@ bool AppInit2()
|
||||
// Try UTXO snapshot first (fast: ~2-10 MB download)
|
||||
bool success = false;
|
||||
bool triedUtxoSnapshot = false;
|
||||
if (needsBootstrap && !fs::exists(dataPath / "txleveldb")) {
|
||||
if (needsBootstrap && !fs::exists(dataPath / GetActiveChainDbDirName())) {
|
||||
uiInterface.InitMessage(_("Downloading UTXO snapshot..."));
|
||||
printf("Bootstrap: trying UTXO snapshot from %s (fast path)...\n", host.c_str());
|
||||
|
||||
@@ -992,13 +992,13 @@ bool AppInit2()
|
||||
#endif
|
||||
|
||||
// ********************************************************* Step 6c: manual UTXO snapshot loading
|
||||
// If utxo-snapshot.bin exists in data dir and no txleveldb, load it.
|
||||
// If utxo-snapshot.bin exists in data dir and no active chain DB, load it.
|
||||
{
|
||||
fs::path dataPath = GetDataDir();
|
||||
fs::path snapshotFile = dataPath / "utxo-snapshot.bin";
|
||||
fs::path txleveldbDir = dataPath / "txleveldb";
|
||||
fs::path chainDbDir = dataPath / GetActiveChainDbDirName();
|
||||
|
||||
if (fs::exists(snapshotFile) && !fs::exists(txleveldbDir)) {
|
||||
if (fs::exists(snapshotFile) && !fs::exists(chainDbDir)) {
|
||||
printf("Found utxo-snapshot.bin — loading UTXO snapshot...\n");
|
||||
uiInterface.InitMessage(_("Loading UTXO snapshot..."));
|
||||
|
||||
@@ -1024,7 +1024,7 @@ bool AppInit2()
|
||||
|
||||
if (GetBoolArg("-loadblockindextest"))
|
||||
{
|
||||
CTxDB txdb("r");
|
||||
CActiveTxDB txdb("r");
|
||||
txdb.LoadBlockIndex();
|
||||
PrintBlockTree();
|
||||
return false;
|
||||
@@ -1037,9 +1037,9 @@ bool AppInit2()
|
||||
{
|
||||
printf("Reindex requested: removing block index database...\n");
|
||||
uiInterface.InitMessage(_("Removing block index for reindex..."));
|
||||
fs::path txleveldbPath = GetDataDir() / "txleveldb";
|
||||
if (fs::exists(txleveldbPath))
|
||||
fs::remove_all(txleveldbPath);
|
||||
fs::path chainDbPath = GetDataDir() / GetActiveChainDbDirName();
|
||||
if (fs::exists(chainDbPath))
|
||||
fs::remove_all(chainDbPath);
|
||||
}
|
||||
|
||||
uiInterface.InitMessage(_("Loading block index..."));
|
||||
@@ -1224,7 +1224,7 @@ bool AppInit2()
|
||||
bool fScannedWithIndex = false;
|
||||
if (fAddressIndex && !GetBoolArg("-rescan"))
|
||||
{
|
||||
CTxDB txdb("r");
|
||||
CActiveTxDB txdb("r");
|
||||
int nAddressIndexStartHeight = 0;
|
||||
uint256 hashAddressIndexBestChain = 0;
|
||||
if (txdb.ReadAddressIndexStartHeight(nAddressIndexStartHeight) &&
|
||||
|
||||
+60
-29
@@ -931,7 +931,7 @@ bool CTransaction::ReadFromDisk(CTxDBBase& txdb, COutPoint prevout)
|
||||
|
||||
bool CTransaction::ReadFromDisk(COutPoint prevout)
|
||||
{
|
||||
CTxDB txdb("r");
|
||||
CActiveTxDB txdb("r");
|
||||
CTxIndex txindex;
|
||||
return ReadFromDisk(txdb, prevout, txindex);
|
||||
}
|
||||
@@ -1064,7 +1064,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
|
||||
{
|
||||
// Load the block this tx is in
|
||||
CTxIndex txindex;
|
||||
if (!CTxDB("r").ReadTxIndex(GetHash(), txindex))
|
||||
if (!CActiveTxDB("r").ReadTxIndex(GetHash(), txindex))
|
||||
return 0;
|
||||
if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos))
|
||||
return 0;
|
||||
@@ -1487,7 +1487,7 @@ bool CMerkleTx::AcceptToMemoryPool(CTxDBBase& txdb, bool fCheckInputs)
|
||||
|
||||
bool CMerkleTx::AcceptToMemoryPool()
|
||||
{
|
||||
CTxDB txdb("r");
|
||||
CActiveTxDB txdb("r");
|
||||
return AcceptToMemoryPool(txdb);
|
||||
}
|
||||
|
||||
@@ -1515,7 +1515,7 @@ bool CWalletTx::AcceptWalletTransaction(CTxDBBase& txdb, bool fCheckInputs)
|
||||
|
||||
bool CWalletTx::AcceptWalletTransaction()
|
||||
{
|
||||
CTxDB txdb("r");
|
||||
CActiveTxDB txdb("r");
|
||||
return AcceptWalletTransaction(txdb);
|
||||
}
|
||||
|
||||
@@ -1548,7 +1548,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
CTxDB txdb("r");
|
||||
CActiveTxDB txdb("r");
|
||||
CTxIndex txindex;
|
||||
if (tx.ReadFromDisk(txdb, COutPoint(hash, 0), txindex))
|
||||
{
|
||||
@@ -1858,7 +1858,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew)
|
||||
if (pindexNew->nChainTrust > nBestInvalidTrust)
|
||||
{
|
||||
nBestInvalidTrust = pindexNew->nChainTrust;
|
||||
CTxDB().WriteBestInvalidTrust(CBigNum(nBestInvalidTrust));
|
||||
CActiveTxDB().WriteBestInvalidTrust(CBigNum(nBestInvalidTrust));
|
||||
uiInterface.NotifyBlocksChanged();
|
||||
}
|
||||
|
||||
@@ -3171,7 +3171,7 @@ bool CBlock::GetCoinAge(uint64_t& nCoinAge) const
|
||||
{
|
||||
nCoinAge = 0;
|
||||
|
||||
CTxDB txdb("r");
|
||||
CActiveTxDB txdb("r");
|
||||
for (const CTransaction& tx : vtx)
|
||||
{
|
||||
uint64_t nTxCoinAge;
|
||||
@@ -3250,7 +3250,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const u
|
||||
pindexNew->phashBlock = &((*mi).first);
|
||||
|
||||
// Write to disk block index
|
||||
CTxDB txdb;
|
||||
CActiveTxDB txdb;
|
||||
if (!txdb.TxnBegin())
|
||||
return false;
|
||||
txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
|
||||
@@ -3897,7 +3897,7 @@ bool LoadBlockIndex(bool fAllowNew)
|
||||
//
|
||||
// Load block index
|
||||
//
|
||||
CTxDB txdb("cr+");
|
||||
CActiveTxDB txdb("cr+");
|
||||
if (!txdb.LoadBlockIndex())
|
||||
return false;
|
||||
|
||||
@@ -4181,7 +4181,7 @@ bool FastImportBlockFile()
|
||||
LOCK(cs_main);
|
||||
CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION);
|
||||
|
||||
CTxDB txdb;
|
||||
CActiveTxDB txdb;
|
||||
txdb.TxnBegin();
|
||||
|
||||
unsigned int nPos = 0;
|
||||
@@ -4783,7 +4783,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
printf("IBD-DIAG: inv received: %d blocks, %d tx from %s (our height=%d)\n",
|
||||
nBlockInv, nTxInv, pfrom->addr.ToString().c_str(), nBestHeight);
|
||||
|
||||
CTxDB txdb("r");
|
||||
CActiveTxDB txdb("r");
|
||||
int nNew = 0, nAlready = 0, nAboveBest = 0;
|
||||
int nFirstInvHeight = -1, nLastInvHeight = -1;
|
||||
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
|
||||
@@ -5129,7 +5129,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
vector<uint256> vWorkQueue;
|
||||
vector<uint256> vEraseQueue;
|
||||
CDataStream vMsg(vRecv);
|
||||
CTxDB txdb("r");
|
||||
CActiveTxDB txdb("r");
|
||||
CTransaction tx;
|
||||
vRecv >> tx;
|
||||
|
||||
@@ -6107,24 +6107,55 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
||||
(int)pto->mapAskFor.size());
|
||||
nLastStallLog = GetTime();
|
||||
}
|
||||
// Use the walk-forward progress point if available, to avoid
|
||||
// restarting from pindexBest (which hits the CBlockLocator
|
||||
// exponential gap and starts the walk-forward from scratch).
|
||||
pto->pindexLastGetBlocksBegin = NULL;
|
||||
if (nHighestInvWalk > nBestHeight && hashHighestInvWalk != 0 &&
|
||||
mapBlockIndex.count(hashHighestInvWalk))
|
||||
// During IBD, avoid falling back to legacy getblocks recovery
|
||||
// anchored at pindexBest or a stale inv walk point. That path can
|
||||
// repeatedly resolve the locator to the same low common ancestor
|
||||
// on a weak peer set, which looks like a sync "freeze" near an
|
||||
// early height (for example around 570) even though the real bug
|
||||
// is the recovery loop. Keep stall recovery header-driven instead
|
||||
// so the planner tip advances from the newest known header state.
|
||||
if (IsInitialBlockDownload())
|
||||
{
|
||||
pto->PushGetBlocks(mapBlockIndex[hashHighestInvWalk], uint256(0));
|
||||
printf("SYNC-DIAG: stall re-request from walk=%d (not best=%d)\n",
|
||||
nHighestInvWalk, nBestHeight);
|
||||
} else {
|
||||
pto->PushGetBlocks(pindexBest, uint256(0));
|
||||
pto->pindexLastGetHeadersBegin = NULL;
|
||||
|
||||
uint256 hashLocatorTip = hashBestHeaderSync;
|
||||
if (hashLocatorTip == 0 && nHighestInvWalk > nBestHeight &&
|
||||
hashHighestInvWalk != 0 && mapBlockIndex.count(hashHighestInvWalk))
|
||||
{
|
||||
hashLocatorTip = hashHighestInvWalk;
|
||||
}
|
||||
|
||||
unsigned int nRefilled = RequestHeaderSyncRefillAllPeers(
|
||||
hashLocatorTip,
|
||||
0,
|
||||
"stall-recovery");
|
||||
unsigned int nQueued = QueueHeaderSyncBlocksParallel(HEADER_DOWNLOAD_WINDOW);
|
||||
|
||||
printf("SYNC-DIAG: stall recovery used headers-first path (locator=%s, refillPeers=%u, queued=%u)\n",
|
||||
hashLocatorTip.ToString().substr(0,20).c_str(),
|
||||
nRefilled,
|
||||
nQueued);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Outside IBD, preserve the older walk-forward getblocks
|
||||
// behavior since we're no longer building out a header planner.
|
||||
pto->pindexLastGetBlocksBegin = NULL;
|
||||
if (nHighestInvWalk > nBestHeight && hashHighestInvWalk != 0 &&
|
||||
mapBlockIndex.count(hashHighestInvWalk))
|
||||
{
|
||||
pto->PushGetBlocks(mapBlockIndex[hashHighestInvWalk], uint256(0));
|
||||
printf("SYNC-DIAG: stall re-request from walk=%d (not best=%d)\n",
|
||||
nHighestInvWalk, nBestHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
pto->PushGetBlocks(pindexBest, uint256(0));
|
||||
}
|
||||
|
||||
pto->pindexLastGetHeadersBegin = NULL;
|
||||
pto->PushGetHeaders(pindexBest, uint256(0));
|
||||
}
|
||||
// Also send getheaders during stall to restart the header planner.
|
||||
// Without this, a drained header cache stays empty because only
|
||||
// getblocks is sent on stall, which can't refill mapHeaderSync.
|
||||
pto->pindexLastGetHeadersBegin = NULL;
|
||||
pto->PushGetHeaders(pindexBest, uint256(0));
|
||||
nLastBlockReceived = GetTime();
|
||||
}
|
||||
}
|
||||
@@ -6212,7 +6243,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
||||
|
||||
vector<CInv> vGetData;
|
||||
int64_t nNow = GetTime() * 1000000;
|
||||
CTxDB txdb("r");
|
||||
CActiveTxDB txdb("r");
|
||||
// During IBD, send larger getdata batches since PoS blocks are small
|
||||
// and the bottleneck is round-trip latency, not bandwidth.
|
||||
unsigned int nGetDataBatchSize = IsInitialBlockDownload() ? 4000 : 1000;
|
||||
|
||||
@@ -80,6 +80,13 @@ public:
|
||||
|
||||
bool IsReadOnly() const { return fReadOnly; }
|
||||
|
||||
// Wrapper accessors for backend forwarding (used by CActiveTxDB).
|
||||
bool ReadRawBytes(const std::string& key, std::string& value) const { return ReadRaw(key, value); }
|
||||
bool WriteRawBytes(const std::string& key, const std::string& value) { return WriteRaw(key, value); }
|
||||
bool EraseRawBytes(const std::string& key) { return EraseRaw(key); }
|
||||
bool ExistsRawBytes(const std::string& key) const { return ExistsRaw(key); }
|
||||
std::unique_ptr<CTxDBIteratorBase> NewRawIterator() const { return NewIterator(); }
|
||||
|
||||
// ── Schema versioning ────────────────────────────────────────────────────
|
||||
bool ReadVersion(int& nVersion);
|
||||
bool WriteVersion(int nVersion);
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2026 The Triangles developers.
|
||||
// Distributed under the MIT/X11 software license.
|
||||
|
||||
#include "txdb.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
bool UseRocksDbBackend()
|
||||
{
|
||||
#ifdef BUILD_ROCKSDB
|
||||
return GetBoolArg("-rocksdb", false);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
const char* GetActiveChainDbBackendName()
|
||||
{
|
||||
#ifdef BUILD_ROCKSDB
|
||||
return UseRocksDbBackend() ? "rocksdb" : "leveldb";
|
||||
#else
|
||||
return "leveldb";
|
||||
#endif
|
||||
}
|
||||
|
||||
const char* GetActiveChainDbDirName()
|
||||
{
|
||||
#ifdef BUILD_ROCKSDB
|
||||
return UseRocksDbBackend() ? "rocksdb" : "txleveldb";
|
||||
#else
|
||||
return "txleveldb";
|
||||
#endif
|
||||
}
|
||||
|
||||
CActiveTxDB::CActiveTxDB(const char* pszMode)
|
||||
{
|
||||
#ifdef BUILD_ROCKSDB
|
||||
if (UseRocksDbBackend())
|
||||
impl.reset(new CRocksTxDB(pszMode));
|
||||
else
|
||||
impl.reset(new CTxDB(pszMode));
|
||||
#else
|
||||
impl.reset(new CTxDB(pszMode));
|
||||
#endif
|
||||
}
|
||||
|
||||
CActiveTxDB::~CActiveTxDB() = default;
|
||||
|
||||
void CActiveTxDB::Close() { impl->Close(); }
|
||||
bool CActiveTxDB::TxnBegin() { return impl->TxnBegin(); }
|
||||
bool CActiveTxDB::TxnCommit() { return impl->TxnCommit(); }
|
||||
bool CActiveTxDB::TxnAbort() { return impl->TxnAbort(); }
|
||||
bool CActiveTxDB::LoadBlockIndex() { return impl->LoadBlockIndex(); }
|
||||
|
||||
bool CActiveTxDB::ReadRaw(const std::string& key, std::string& value) const { return impl->ReadRawBytes(key, value); }
|
||||
bool CActiveTxDB::WriteRaw(const std::string& key, const std::string& value) { return impl->WriteRawBytes(key, value); }
|
||||
bool CActiveTxDB::EraseRaw(const std::string& key) { return impl->EraseRawBytes(key); }
|
||||
bool CActiveTxDB::ExistsRaw(const std::string& key) const { return impl->ExistsRawBytes(key); }
|
||||
std::unique_ptr<CTxDBIteratorBase> CActiveTxDB::NewIterator() const { return impl->NewRawIterator(); }
|
||||
+32
@@ -7,5 +7,37 @@
|
||||
#define TRIANGLES_TXDB_H
|
||||
|
||||
#include "txdb-leveldb.h"
|
||||
#ifdef BUILD_ROCKSDB
|
||||
#include "txdb-rocksdb.h"
|
||||
#endif
|
||||
|
||||
#include <memory>
|
||||
|
||||
bool UseRocksDbBackend();
|
||||
const char* GetActiveChainDbBackendName();
|
||||
const char* GetActiveChainDbDirName();
|
||||
|
||||
class CActiveTxDB final : public CTxDBBase
|
||||
{
|
||||
public:
|
||||
explicit CActiveTxDB(const char* pszMode = "r+");
|
||||
~CActiveTxDB() override;
|
||||
|
||||
void Close() override;
|
||||
bool TxnBegin() override;
|
||||
bool TxnCommit() override;
|
||||
bool TxnAbort() override;
|
||||
bool LoadBlockIndex() override;
|
||||
|
||||
protected:
|
||||
bool ReadRaw(const std::string& key, std::string& value) const override;
|
||||
bool WriteRaw(const std::string& key, const std::string& value) override;
|
||||
bool EraseRaw(const std::string& key) override;
|
||||
bool ExistsRaw(const std::string& key) const override;
|
||||
std::unique_ptr<CTxDBIteratorBase> NewIterator() const override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<CTxDBBase> impl;
|
||||
};
|
||||
|
||||
#endif // TRIANGLES_TXDB_H
|
||||
|
||||
+119
-44
@@ -16,6 +16,13 @@
|
||||
#include <leveldb/write_batch.h>
|
||||
#include <leveldb/cache.h>
|
||||
#include <leveldb/filter_policy.h>
|
||||
#ifdef BUILD_ROCKSDB
|
||||
#include <rocksdb/db.h>
|
||||
#include <rocksdb/write_batch.h>
|
||||
#include <rocksdb/cache.h>
|
||||
#include <rocksdb/filter_policy.h>
|
||||
#include <rocksdb/table.h>
|
||||
#endif
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
@@ -64,7 +71,7 @@ bool DumpSnapshot(const fs::path& destPath,
|
||||
// Count UTXOs first
|
||||
int nUtxoCount = 0;
|
||||
{
|
||||
CTxDB txdbRead("r");
|
||||
CActiveTxDB txdbRead("r");
|
||||
txdbRead.SumUtxoValues(nUtxoCount);
|
||||
}
|
||||
|
||||
@@ -271,47 +278,104 @@ bool LoadSnapshot(const fs::path& snapshotPath,
|
||||
printf("UtxoSnapshot: loading snapshot at height %d (%d headers, %d UTXOs)\n",
|
||||
height, numHeaders, numUtxos);
|
||||
|
||||
// Create fresh LevelDB directory
|
||||
fs::path txleveldbPath = dataDir / "txleveldb";
|
||||
if (fs::exists(txleveldbPath))
|
||||
fs::remove_all(txleveldbPath);
|
||||
fs::create_directories(txleveldbPath);
|
||||
// Create fresh chain DB directory for the active backend.
|
||||
const bool useRocksDb = UseRocksDbBackend();
|
||||
fs::path chainDbPath = dataDir / GetActiveChainDbDirName();
|
||||
if (fs::exists(chainDbPath))
|
||||
fs::remove_all(chainDbPath);
|
||||
fs::create_directories(chainDbPath);
|
||||
|
||||
// Open LevelDB directly (not via CTxDB - it's not initialized yet)
|
||||
leveldb::Options options;
|
||||
int nCacheSizeMB = GetArg("-dbcache", 2048);
|
||||
options.block_cache = leveldb::NewLRUCache(nCacheSizeMB * 1048576);
|
||||
options.filter_policy = leveldb::NewBloomFilterPolicy(10);
|
||||
options.write_buffer_size = 64 * 1048576;
|
||||
options.max_open_files = 1000;
|
||||
options.create_if_missing = true;
|
||||
|
||||
leveldb::DB* pdb = NULL;
|
||||
leveldb::Status status = leveldb::DB::Open(options, txleveldbPath.string(), &pdb);
|
||||
if (!status.ok()) {
|
||||
leveldb::Options options;
|
||||
options.block_cache = NULL;
|
||||
options.filter_policy = NULL;
|
||||
#ifdef BUILD_ROCKSDB
|
||||
rocksdb::DB* rdb = NULL;
|
||||
std::shared_ptr<rocksdb::Cache> rocksCache;
|
||||
rocksdb::BlockBasedTableOptions rocksTableOptions;
|
||||
#endif
|
||||
|
||||
if (useRocksDb) {
|
||||
#ifdef BUILD_ROCKSDB
|
||||
rocksdb::Options rocksOptions;
|
||||
rocksOptions.create_if_missing = true;
|
||||
rocksOptions.compression = rocksdb::kSnappyCompression;
|
||||
rocksOptions.write_buffer_size = 64 * 1048576;
|
||||
rocksOptions.max_open_files = 1000;
|
||||
rocksCache = rocksdb::NewLRUCache(static_cast<size_t>(nCacheSizeMB) * 1048576);
|
||||
rocksTableOptions.block_cache = rocksCache;
|
||||
rocksTableOptions.filter_policy.reset(rocksdb::NewBloomFilterPolicy(10, false));
|
||||
rocksOptions.table_factory.reset(rocksdb::NewBlockBasedTableFactory(rocksTableOptions));
|
||||
rocksdb::Status rocksStatus = rocksdb::DB::Open(rocksOptions, chainDbPath.string(), &rdb);
|
||||
if (!rocksStatus.ok()) {
|
||||
fclose(file);
|
||||
strError = "Cannot create RocksDB: " + rocksStatus.ToString();
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
fclose(file);
|
||||
delete options.filter_policy;
|
||||
delete options.block_cache;
|
||||
strError = "Cannot create LevelDB: " + status.ToString();
|
||||
strError = "RocksDB snapshot load requested but binary was built without RocksDB";
|
||||
return false;
|
||||
#endif
|
||||
} else {
|
||||
options.block_cache = leveldb::NewLRUCache(nCacheSizeMB * 1048576);
|
||||
options.filter_policy = leveldb::NewBloomFilterPolicy(10);
|
||||
options.write_buffer_size = 64 * 1048576;
|
||||
options.max_open_files = 1000;
|
||||
options.create_if_missing = true;
|
||||
|
||||
leveldb::Status status = leveldb::DB::Open(options, chainDbPath.string(), &pdb);
|
||||
if (!status.ok()) {
|
||||
fclose(file);
|
||||
delete options.filter_policy;
|
||||
delete options.block_cache;
|
||||
strError = "Cannot create LevelDB: " + status.ToString();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SHA256_CTX sha256;
|
||||
SHA256_Init(&sha256);
|
||||
|
||||
leveldb::WriteBatch batch;
|
||||
bool success = true;
|
||||
unsigned int nBatchSize = 0;
|
||||
leveldb::WriteBatch batch;
|
||||
#ifdef BUILD_ROCKSDB
|
||||
rocksdb::WriteBatch rocksBatch;
|
||||
#endif
|
||||
|
||||
auto batchPut = [&](const std::string& key, const std::string& value) {
|
||||
if (useRocksDb) {
|
||||
#ifdef BUILD_ROCKSDB
|
||||
rocksBatch.Put(key, value);
|
||||
#endif
|
||||
} else {
|
||||
batch.Put(key, value);
|
||||
}
|
||||
nBatchSize++;
|
||||
};
|
||||
|
||||
auto flushBatch = [&]() -> bool {
|
||||
if (nBatchSize == 0)
|
||||
return true;
|
||||
leveldb::Status s = pdb->Write(leveldb::WriteOptions(), &batch);
|
||||
if (!s.ok()) {
|
||||
strError = "LevelDB write failed: " + s.ToString();
|
||||
return false;
|
||||
if (useRocksDb) {
|
||||
#ifdef BUILD_ROCKSDB
|
||||
rocksdb::Status s = rdb->Write(rocksdb::WriteOptions(), &rocksBatch);
|
||||
if (!s.ok()) {
|
||||
strError = "RocksDB write failed: " + s.ToString();
|
||||
return false;
|
||||
}
|
||||
rocksBatch.Clear();
|
||||
#endif
|
||||
} else {
|
||||
leveldb::Status s = pdb->Write(leveldb::WriteOptions(), &batch);
|
||||
if (!s.ok()) {
|
||||
strError = "LevelDB write failed: " + s.ToString();
|
||||
return false;
|
||||
}
|
||||
batch.Clear();
|
||||
}
|
||||
batch.Clear();
|
||||
nBatchSize = 0;
|
||||
return true;
|
||||
};
|
||||
@@ -352,8 +416,7 @@ bool LoadSnapshot(const fs::path& snapshotPath,
|
||||
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
|
||||
ssValue << diskindex;
|
||||
|
||||
batch.Put(ssKey.str(), ssValue.str());
|
||||
nBatchSize++;
|
||||
batchPut(ssKey.str(), ssValue.str());
|
||||
|
||||
if (nBatchSize >= 1000) {
|
||||
if (!flushBatch()) { success = false; break; }
|
||||
@@ -401,8 +464,7 @@ bool LoadSnapshot(const fs::path& snapshotPath,
|
||||
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
|
||||
ssValue << entry;
|
||||
|
||||
batch.Put(ssKey.str(), ssValue.str());
|
||||
nBatchSize++;
|
||||
batchPut(ssKey.str(), ssValue.str());
|
||||
|
||||
if (nBatchSize >= 50000) {
|
||||
if (!flushBatch()) { success = false; break; }
|
||||
@@ -433,37 +495,50 @@ bool LoadSnapshot(const fs::path& snapshotPath,
|
||||
|
||||
// Write metadata
|
||||
if (success) {
|
||||
leveldb::WriteBatch metaBatch;
|
||||
|
||||
// hashBestChain
|
||||
CDataStream ssKey1(SER_DISK, CLIENT_VERSION);
|
||||
ssKey1 << std::string("hashBestChain");
|
||||
CDataStream ssVal1(SER_DISK, CLIENT_VERSION);
|
||||
ssVal1 << blockHash;
|
||||
metaBatch.Put(ssKey1.str(), ssVal1.str());
|
||||
|
||||
// dbformat = 3
|
||||
CDataStream ssKey2(SER_DISK, CLIENT_VERSION);
|
||||
ssKey2 << std::string("dbformat");
|
||||
CDataStream ssVal2(SER_DISK, CLIENT_VERSION);
|
||||
ssVal2 << (int)3;
|
||||
metaBatch.Put(ssKey2.str(), ssVal2.str());
|
||||
|
||||
// version
|
||||
CDataStream ssKey3(SER_DISK, CLIENT_VERSION);
|
||||
ssKey3 << std::string("version");
|
||||
CDataStream ssVal3(SER_DISK, CLIENT_VERSION);
|
||||
ssVal3 << DATABASE_VERSION;
|
||||
metaBatch.Put(ssKey3.str(), ssVal3.str());
|
||||
|
||||
leveldb::Status s = pdb->Write(leveldb::WriteOptions(), &metaBatch);
|
||||
if (!s.ok()) {
|
||||
success = false;
|
||||
strError = "Failed to write metadata: " + s.ToString();
|
||||
if (useRocksDb) {
|
||||
#ifdef BUILD_ROCKSDB
|
||||
rocksdb::WriteBatch metaBatch;
|
||||
metaBatch.Put(ssKey1.str(), ssVal1.str());
|
||||
metaBatch.Put(ssKey2.str(), ssVal2.str());
|
||||
metaBatch.Put(ssKey3.str(), ssVal3.str());
|
||||
rocksdb::Status s = rdb->Write(rocksdb::WriteOptions(), &metaBatch);
|
||||
if (!s.ok()) {
|
||||
success = false;
|
||||
strError = "Failed to write RocksDB metadata: " + s.ToString();
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
leveldb::WriteBatch metaBatch;
|
||||
metaBatch.Put(ssKey1.str(), ssVal1.str());
|
||||
metaBatch.Put(ssKey2.str(), ssVal2.str());
|
||||
metaBatch.Put(ssKey3.str(), ssVal3.str());
|
||||
leveldb::Status s = pdb->Write(leveldb::WriteOptions(), &metaBatch);
|
||||
if (!s.ok()) {
|
||||
success = false;
|
||||
strError = "Failed to write metadata: " + s.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up LevelDB
|
||||
// Clean up chain DB
|
||||
#ifdef BUILD_ROCKSDB
|
||||
delete rdb;
|
||||
#endif
|
||||
delete pdb;
|
||||
delete options.filter_policy;
|
||||
delete options.block_cache;
|
||||
@@ -473,8 +548,8 @@ bool LoadSnapshot(const fs::path& snapshotPath,
|
||||
if (!success) {
|
||||
// Remove corrupted/incomplete database
|
||||
printf("UtxoSnapshot: load failed: %s\n", strError.c_str());
|
||||
if (fs::exists(txleveldbPath))
|
||||
fs::remove_all(txleveldbPath);
|
||||
if (fs::exists(chainDbPath))
|
||||
fs::remove_all(chainDbPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user