mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-28 01:20:32 +00:00
test: Migrate nodestore tests from Beast to GTest (#7292)
Co-authored-by: Marek Foss <marek.foss@neti-soft.com> Co-authored-by: Alex Kremer <akremer@ripple.com>
This commit is contained in:
committed by
GitHub
parent
20801d98ac
commit
29120dfcbd
@@ -130,12 +130,9 @@ test.ledger > xrpl.json
|
||||
test.ledger > xrpl.ledger
|
||||
test.ledger > xrpl.protocol
|
||||
test.nodestore > test.jtx
|
||||
test.nodestore > test.unit_test
|
||||
test.nodestore > xrpl.basics
|
||||
test.nodestore > xrpl.config
|
||||
test.nodestore > xrpld.core
|
||||
test.nodestore > xrpl.nodestore
|
||||
test.nodestore > xrpl.protocol
|
||||
test.nodestore > xrpl.rdb
|
||||
test.overlay > test.jtx
|
||||
test.overlay > test.unit_test
|
||||
|
||||
3
.github/scripts/rename/namespace.sh
vendored
3
.github/scripts/rename/namespace.sh
vendored
@@ -46,9 +46,6 @@ for DIRECTORY in "${DIRECTORIES[@]}"; do
|
||||
done
|
||||
done
|
||||
|
||||
# Special case for NuDBFactory that has ripple twice in the test suite name.
|
||||
${SED_COMMAND} -i -E 's/(BEAST_DEFINE_TESTSUITE.+)ripple(.+)/\1xrpl\2/g' src/test/nodestore/NuDBFactory_test.cpp
|
||||
|
||||
DIRECTORY=$1
|
||||
find "${DIRECTORY}" -type f -name "*.md" | while read -r FILE; do
|
||||
echo "Processing file: ${FILE}"
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
namespace xrpl {
|
||||
|
||||
// Forward declarations
|
||||
namespace NodeStore {
|
||||
namespace node_store {
|
||||
class Database;
|
||||
} // namespace NodeStore
|
||||
} // namespace node_store
|
||||
namespace Resource {
|
||||
class Manager;
|
||||
} // namespace Resource
|
||||
@@ -164,7 +164,7 @@ public:
|
||||
getResourceManager() = 0;
|
||||
|
||||
// Storage services
|
||||
virtual NodeStore::Database&
|
||||
virtual node_store::Database&
|
||||
getNodeStore() = 0;
|
||||
|
||||
virtual SHAMapStore&
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
/**
|
||||
* A backend used for the NodeStore.
|
||||
@@ -163,4 +163,4 @@ public:
|
||||
fdRequired() const = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace xrpl {
|
||||
class Section;
|
||||
} // namespace xrpl
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
/**
|
||||
* Persistency layer for NodeObject
|
||||
@@ -248,7 +248,7 @@ protected:
|
||||
void
|
||||
storeStats(std::uint64_t count, std::uint64_t sz)
|
||||
{
|
||||
XRPL_ASSERT(count <= sz, "xrpl::NodeStore::Database::storeStats : valid inputs");
|
||||
XRPL_ASSERT(count <= sz, "xrpl::node_store::Database::storeStats : valid inputs");
|
||||
storeCount_ += count;
|
||||
storeSz_ += sz;
|
||||
}
|
||||
@@ -308,4 +308,4 @@ private:
|
||||
threadEntry();
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
/* This class has two key-value store Backend objects for persisting SHAMap
|
||||
* records. This facilitates online deletion of data. New backends are
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
*/
|
||||
virtual void
|
||||
rotate(
|
||||
std::unique_ptr<NodeStore::Backend>&& newBackend,
|
||||
std::unique_ptr<node_store::Backend>&& newBackend,
|
||||
std::function<void(std::string const& writableName, std::string const& archiveName)> const&
|
||||
f) = 0;
|
||||
|
||||
@@ -56,4 +56,4 @@ public:
|
||||
setRotationInFlight(bool inFlight) = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <xrpl/nodestore/Scheduler.h>
|
||||
#include <xrpl/nodestore/Task.h>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
/**
|
||||
* Simple NodeStore Scheduler that just performs the tasks synchronously.
|
||||
@@ -21,4 +21,4 @@ public:
|
||||
onBatchWrite(BatchWriteReport const& report) override;
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace xrpl {
|
||||
class Section;
|
||||
} // namespace xrpl
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
/**
|
||||
* Base class for backend factories.
|
||||
@@ -70,4 +70,4 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
/**
|
||||
* Singleton for managing NodeStore factories and back ends.
|
||||
@@ -98,4 +98,4 @@ public:
|
||||
beast::Journal journal) = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
// VFALCO NOTE Intentionally not in the NodeStore namespace
|
||||
// VFALCO NOTE Intentionally not in the node_store namespace
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
enum class FetchType { Synchronous, Async };
|
||||
|
||||
@@ -71,4 +71,4 @@ public:
|
||||
onBatchWrite(BatchWriteReport const& report) = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
/**
|
||||
* Derived classes perform scheduled tasks.
|
||||
@@ -17,4 +17,4 @@ struct Task
|
||||
performScheduledTask() = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
// This is only used to pre-allocate the array for
|
||||
// batch objects and does not affect the amount written.
|
||||
@@ -36,4 +36,4 @@ enum class Status {
|
||||
*/
|
||||
using Batch = std::vector<std::shared_ptr<NodeObject>>;
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
/**
|
||||
* Batch-writing assist logic.
|
||||
@@ -86,4 +86,4 @@ private:
|
||||
Batch writeSet_;
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
class DatabaseNodeImp : public Database
|
||||
{
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
|
||||
XRPL_ASSERT(
|
||||
backend_,
|
||||
"xrpl::NodeStore::DatabaseNodeImp::DatabaseNodeImp : non-null "
|
||||
"xrpl::node_store::DatabaseNodeImp::DatabaseNodeImp : non-null "
|
||||
"backend");
|
||||
}
|
||||
|
||||
@@ -138,4 +138,4 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
class DatabaseRotatingImp : public DatabaseRotating
|
||||
{
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
|
||||
void
|
||||
rotate(
|
||||
std::unique_ptr<NodeStore::Backend>&& newBackend,
|
||||
std::unique_ptr<node_store::Backend>&& newBackend,
|
||||
std::function<void(std::string const& writableName, std::string const& archiveName)> const&
|
||||
f) override;
|
||||
|
||||
@@ -94,4 +94,4 @@ private:
|
||||
forEach(std::function<void(std::shared_ptr<NodeObject>)> f) override;
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
/**
|
||||
* Parsed key/value blob into NodeObject components.
|
||||
@@ -49,4 +49,4 @@ private:
|
||||
int dataBytes_;
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
/**
|
||||
* Convert a NodeObject from in-memory to database format.
|
||||
@@ -68,7 +68,7 @@ class EncodedBlob
|
||||
public:
|
||||
explicit EncodedBlob(std::shared_ptr<NodeObject> const& obj)
|
||||
: size_([&obj]() {
|
||||
XRPL_ASSERT(obj, "xrpl::NodeStore::EncodedBlob::EncodedBlob : non-null input");
|
||||
XRPL_ASSERT(obj, "xrpl::node_store::EncodedBlob::EncodedBlob : non-null input");
|
||||
|
||||
if (!obj)
|
||||
throw std::runtime_error("EncodedBlob: unseated std::shared_ptr used.");
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
XRPL_ASSERT(
|
||||
((ptr_ == payload_.data()) && (size_ <= payload_.size())) ||
|
||||
((ptr_ != payload_.data()) && (size_ > payload_.size())),
|
||||
"xrpl::NodeStore::EncodedBlob::~EncodedBlob : valid payload "
|
||||
"xrpl::node_store::EncodedBlob::~EncodedBlob : valid payload "
|
||||
"pointer");
|
||||
|
||||
if (ptr_ != payload_.data())
|
||||
@@ -114,4 +114,4 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
class ManagerImp : public Manager
|
||||
{
|
||||
@@ -57,4 +57,4 @@ public:
|
||||
beast::Journal journal) override;
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
template <class BufferFactory>
|
||||
std::pair<void const*, std::size_t>
|
||||
@@ -269,7 +269,7 @@ nodeobjectCompress(void const* in, std::size_t inSize, BufferFactory&& bf)
|
||||
case 1: // lz4
|
||||
{
|
||||
std::uint8_t* p = nullptr;
|
||||
auto const lzr = NodeStore::lz4Compress(in, inSize, [&p, &vn, &bf](std::size_t n) {
|
||||
auto const lzr = node_store::lz4Compress(in, inSize, [&p, &vn, &bf](std::size_t n) {
|
||||
p = reinterpret_cast<std::uint8_t*>(bf(vn + n));
|
||||
return p + vn;
|
||||
});
|
||||
@@ -316,4 +316,4 @@ filterInner(void* in, std::size_t inSize)
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
// This is a variant of the base128 varint format from
|
||||
// google protocol buffers:
|
||||
@@ -123,4 +123,4 @@ write(nudb::detail::ostream& os, std::size_t t)
|
||||
writeVarint(os.data(sizeVarint(t)), t);
|
||||
}
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -26,10 +26,10 @@ public:
|
||||
explicit Family() = default;
|
||||
virtual ~Family() = default;
|
||||
|
||||
virtual NodeStore::Database&
|
||||
virtual node_store::Database&
|
||||
db() = 0;
|
||||
|
||||
[[nodiscard]] virtual NodeStore::Database const&
|
||||
[[nodiscard]] virtual node_store::Database const&
|
||||
db() const = 0;
|
||||
|
||||
virtual beast::Journal const&
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
namespace {
|
||||
|
||||
constexpr std::size_t kPoolSizes[] = {1000, 10000, 100000};
|
||||
@@ -326,4 +326,4 @@ registerStoreBatch(BackendConfig const& bc)
|
||||
}();
|
||||
|
||||
} // namespace
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
namespace {
|
||||
|
||||
// Number of distinct objects pre-generated per run.
|
||||
@@ -240,4 +240,4 @@ registerWorkload(BackendConfig const& bc, Workload const& w)
|
||||
}();
|
||||
|
||||
} // namespace
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
// Shared helpers for the NodeStore benchmarks.
|
||||
//
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
// Fill `bytes` of memory at `buffer` with random bits drawn from `g`.
|
||||
template <class Generator>
|
||||
@@ -315,4 +315,4 @@ backendConfigs()
|
||||
return kConfigs;
|
||||
}
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
BatchWriter::BatchWriter(Callback& callback, Scheduler& scheduler)
|
||||
: callback_(callback), scheduler_(scheduler)
|
||||
@@ -72,7 +72,7 @@ BatchWriter::writeBatch()
|
||||
|
||||
writeSet_.swap(set);
|
||||
XRPL_ASSERT(
|
||||
writeSet_.empty(), "xrpl::NodeStore::BatchWriter::writeBatch : writes not set");
|
||||
writeSet_.empty(), "xrpl::node_store::BatchWriter::writeBatch : writes not set");
|
||||
writeLoad_ = set.size();
|
||||
|
||||
if (set.empty())
|
||||
@@ -107,4 +107,4 @@ BatchWriter::waitForWriting()
|
||||
writeCondition_.wait(sl);
|
||||
}
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
Database::Database(
|
||||
Scheduler& scheduler,
|
||||
@@ -43,7 +43,7 @@ Database::Database(
|
||||
, requestBundle_(get<int>(config, Keys::kRqBundle, 4))
|
||||
, readThreads_(std::max(1, readThreads))
|
||||
{
|
||||
XRPL_ASSERT(readThreads, "xrpl::NodeStore::Database::Database : nonzero threads input");
|
||||
XRPL_ASSERT(readThreads, "xrpl::node_store::Database::Database : nonzero threads input");
|
||||
|
||||
if (earliestLedgerSeq_ < 1)
|
||||
Throw<std::runtime_error>("Invalid earliest_seq");
|
||||
@@ -89,7 +89,7 @@ Database::Database(
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
!it->second.empty(),
|
||||
"xrpl::NodeStore::Database::Database : non-empty "
|
||||
"xrpl::node_store::Database::Database : non-empty "
|
||||
"data");
|
||||
|
||||
auto const& hash = it->first;
|
||||
@@ -164,7 +164,7 @@ Database::stop()
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
steady_clock::now() - start < 30s,
|
||||
"xrpl::NodeStore::Database::stop : maximum stop duration");
|
||||
"xrpl::node_store::Database::stop : maximum stop duration");
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ Database::importInternal(Backend& dstBackend, Database& srcDB)
|
||||
};
|
||||
|
||||
srcDB.forEach([&](std::shared_ptr<NodeObject> nodeObject) {
|
||||
XRPL_ASSERT(nodeObject, "xrpl::NodeStore::Database::importInternal : non-null node");
|
||||
XRPL_ASSERT(nodeObject, "xrpl::node_store::Database::importInternal : non-null node");
|
||||
if (!nodeObject) // This should never happen
|
||||
return;
|
||||
|
||||
@@ -257,7 +257,7 @@ Database::fetchNodeObject(
|
||||
void
|
||||
Database::getCountsJson(json::Value& obj)
|
||||
{
|
||||
XRPL_ASSERT(obj.isObject(), "xrpl::NodeStore::Database::getCountsJson : valid input type");
|
||||
XRPL_ASSERT(obj.isObject(), "xrpl::node_store::Database::getCountsJson : valid input type");
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> const lock(readLock_);
|
||||
@@ -276,4 +276,4 @@ Database::getCountsJson(json::Value& obj)
|
||||
obj[jss::node_reads_duration_us] = std::to_string(fetchDurationUs_);
|
||||
}
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
void
|
||||
DatabaseNodeImp::store(NodeObjectType type, Blob&& data, uint256 const& hash, std::uint32_t)
|
||||
@@ -125,4 +125,4 @@ DatabaseNodeImp::fetchNodeObject(
|
||||
return nodeObject;
|
||||
}
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
DatabaseRotatingImp::DatabaseRotatingImp(
|
||||
Scheduler& scheduler,
|
||||
@@ -43,7 +43,7 @@ DatabaseRotatingImp::DatabaseRotatingImp(
|
||||
|
||||
void
|
||||
DatabaseRotatingImp::rotate(
|
||||
std::unique_ptr<NodeStore::Backend>&& newBackend,
|
||||
std::unique_ptr<node_store::Backend>&& newBackend,
|
||||
std::function<void(std::string const& writableName, std::string const& archiveName)> const& f)
|
||||
{
|
||||
// Pass these two names to the callback function
|
||||
@@ -52,7 +52,7 @@ DatabaseRotatingImp::rotate(
|
||||
// Hold on to current archive backend pointer until after the
|
||||
// callback finishes. Only then will the archive directory be
|
||||
// deleted.
|
||||
std::shared_ptr<NodeStore::Backend> oldArchiveBackend;
|
||||
std::shared_ptr<node_store::Backend> oldArchiveBackend;
|
||||
std::uint64_t copyForwards = 0;
|
||||
{
|
||||
std::scoped_lock const lock(mutex_);
|
||||
@@ -232,4 +232,4 @@ DatabaseRotatingImp::forEach(std::function<void(std::shared_ptr<NodeObject>)> f)
|
||||
archive->forEach(f);
|
||||
}
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
DecodedBlob::DecodedBlob(void const* key, void const* value, int valueBytes) : key_(key)
|
||||
{
|
||||
@@ -55,7 +55,7 @@ DecodedBlob::DecodedBlob(void const* key, void const* value, int valueBytes) : k
|
||||
std::shared_ptr<NodeObject>
|
||||
DecodedBlob::createObject()
|
||||
{
|
||||
XRPL_ASSERT(success_, "xrpl::NodeStore::DecodedBlob::createObject : valid object type");
|
||||
XRPL_ASSERT(success_, "xrpl::node_store::DecodedBlob::createObject : valid object type");
|
||||
|
||||
std::shared_ptr<NodeObject> object;
|
||||
|
||||
@@ -69,4 +69,4 @@ DecodedBlob::createObject()
|
||||
return object;
|
||||
}
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <xrpl/nodestore/Scheduler.h>
|
||||
#include <xrpl/nodestore/Task.h>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
void
|
||||
DummyScheduler::scheduleTask(Task& task)
|
||||
@@ -22,4 +22,4 @@ DummyScheduler::onBatchWrite(BatchWriteReport const& report)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
ManagerImp&
|
||||
ManagerImp::instance()
|
||||
@@ -112,7 +112,7 @@ ManagerImp::erase(Factory& factory)
|
||||
std::scoped_lock const _(mutex_);
|
||||
auto const iter =
|
||||
std::ranges::find_if(list_, [&factory](Factory* other) { return other == &factory; });
|
||||
XRPL_ASSERT(iter != list_.end(), "xrpl::NodeStore::ManagerImp::erase : valid input");
|
||||
XRPL_ASSERT(iter != list_.end(), "xrpl::node_store::ManagerImp::erase : valid input");
|
||||
list_.erase(iter);
|
||||
}
|
||||
|
||||
@@ -135,4 +135,4 @@ Manager::instance()
|
||||
return ManagerImp::instance();
|
||||
}
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
struct MemoryDB
|
||||
{
|
||||
@@ -132,7 +132,7 @@ public:
|
||||
Status
|
||||
fetch(uint256 const& hash, std::shared_ptr<NodeObject>* pObject) override
|
||||
{
|
||||
XRPL_ASSERT(db_, "xrpl::NodeStore::MemoryBackend::fetch : non-null database");
|
||||
XRPL_ASSERT(db_, "xrpl::node_store::MemoryBackend::fetch : non-null database");
|
||||
|
||||
std::scoped_lock const _(db_->mutex);
|
||||
|
||||
@@ -149,7 +149,7 @@ public:
|
||||
void
|
||||
store(std::shared_ptr<NodeObject> const& object) override
|
||||
{
|
||||
XRPL_ASSERT(db_, "xrpl::NodeStore::MemoryBackend::store : non-null database");
|
||||
XRPL_ASSERT(db_, "xrpl::node_store::MemoryBackend::store : non-null database");
|
||||
std::scoped_lock const _(db_->mutex);
|
||||
db_->table.emplace(object->getHash(), object);
|
||||
}
|
||||
@@ -169,7 +169,7 @@ public:
|
||||
void
|
||||
forEach(std::function<void(std::shared_ptr<NodeObject>)> f) override
|
||||
{
|
||||
XRPL_ASSERT(db_, "xrpl::NodeStore::MemoryBackend::forEach : non-null database");
|
||||
XRPL_ASSERT(db_, "xrpl::node_store::MemoryBackend::forEach : non-null database");
|
||||
for (auto const& e : db_->table)
|
||||
f(e.second);
|
||||
}
|
||||
@@ -216,4 +216,4 @@ MemoryFactory::createInstance(
|
||||
return std::make_unique<MemoryBackend>(keyBytes, keyValues, journal);
|
||||
}
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
class NuDBBackend : public Backend
|
||||
{
|
||||
@@ -136,7 +136,7 @@ public:
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE(
|
||||
"xrpl::NodeStore::NuDBBackend::open : database is already "
|
||||
"xrpl::node_store::NuDBBackend::open : database is already "
|
||||
"open");
|
||||
JLOG(j.error()) << "database is already open";
|
||||
return;
|
||||
@@ -441,4 +441,4 @@ registerNuDBFactory(Manager& manager)
|
||||
static NuDBFactory const kInstance{manager};
|
||||
}
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
class NullBackend : public Backend
|
||||
{
|
||||
@@ -125,4 +125,4 @@ registerNullFactory(Manager& manager)
|
||||
static NullFactory const kInstance{manager};
|
||||
}
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
class RocksDBEnv : public rocksdb::EnvWrapper
|
||||
{
|
||||
@@ -231,7 +231,7 @@ public:
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
UNREACHABLE(
|
||||
"xrpl::NodeStore::RocksDBBackend::open : database is already "
|
||||
"xrpl::node_store::RocksDBBackend::open : database is already "
|
||||
"open");
|
||||
JLOG(journal.error()) << "database is already open";
|
||||
return;
|
||||
@@ -279,7 +279,7 @@ public:
|
||||
Status
|
||||
fetch(uint256 const& hash, std::shared_ptr<NodeObject>* pObject) override
|
||||
{
|
||||
XRPL_ASSERT(db, "xrpl::NodeStore::RocksDBBackend::fetch : non-null database");
|
||||
XRPL_ASSERT(db, "xrpl::node_store::RocksDBBackend::fetch : non-null database");
|
||||
pObject->reset();
|
||||
|
||||
Status status = Status::Ok;
|
||||
@@ -339,7 +339,7 @@ public:
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
db,
|
||||
"xrpl::NodeStore::RocksDBBackend::storeBatch : non-null "
|
||||
"xrpl::node_store::RocksDBBackend::storeBatch : non-null "
|
||||
"database");
|
||||
rocksdb::WriteBatch wb;
|
||||
|
||||
@@ -369,7 +369,7 @@ public:
|
||||
void
|
||||
forEach(std::function<void(std::shared_ptr<NodeObject>)> f) override
|
||||
{
|
||||
XRPL_ASSERT(db, "xrpl::NodeStore::RocksDBBackend::forEach : non-null database");
|
||||
XRPL_ASSERT(db, "xrpl::node_store::RocksDBBackend::forEach : non-null database");
|
||||
rocksdb::ReadOptions const options;
|
||||
|
||||
std::unique_ptr<rocksdb::Iterator> it(db->NewIterator(options));
|
||||
@@ -468,6 +468,6 @@ registerRocksDBFactory(Manager& manager)
|
||||
static RocksDBFactory const kInstance{manager};
|
||||
}
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
#endif
|
||||
|
||||
@@ -489,7 +489,7 @@ public:
|
||||
lastRotated = ledgerSeq - 1;
|
||||
}
|
||||
|
||||
std::unique_ptr<NodeStore::Backend>
|
||||
std::unique_ptr<node_store::Backend>
|
||||
makeBackendRotating(jtx::Env& env, NodeStoreScheduler& scheduler, std::string path)
|
||||
{
|
||||
Section section{env.app().config().section(Sections::kNodeDatabase)};
|
||||
@@ -500,7 +500,7 @@ public:
|
||||
newPath = path;
|
||||
section.set(Keys::kPath, newPath.string());
|
||||
|
||||
auto backend{NodeStore::Manager::instance().makeBackend(
|
||||
auto backend{node_store::Manager::instance().makeBackend(
|
||||
section,
|
||||
megabytes(env.app().config().getValueFor(SizedItem::BurstSize, std::nullopt)),
|
||||
scheduler,
|
||||
@@ -549,7 +549,7 @@ public:
|
||||
auto archiveBackend = makeBackendRotating(env, scheduler, archiveDb);
|
||||
|
||||
static constexpr int kReadThreads = 4;
|
||||
auto dbr = std::make_unique<NodeStore::DatabaseRotatingImp>(
|
||||
auto dbr = std::make_unique<node_store::DatabaseRotatingImp>(
|
||||
scheduler,
|
||||
kReadThreads,
|
||||
std::move(writableBackend),
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
#include <test/nodestore/TestBase.h>
|
||||
#include <test/unit_test/SuiteJournal.h>
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/temp_dir.h>
|
||||
#include <xrpl/beast/xor_shift_engine.h>
|
||||
#include <xrpl/config/BasicConfig.h>
|
||||
#include <xrpl/config/Constants.h>
|
||||
#include <xrpl/nodestore/Backend.h>
|
||||
#include <xrpl/nodestore/DummyScheduler.h>
|
||||
#include <xrpl/nodestore/Manager.h>
|
||||
#include <xrpl/nodestore/Types.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
|
||||
// Tests the Backend interface
|
||||
//
|
||||
class Backend_test : public TestBase
|
||||
{
|
||||
public:
|
||||
void
|
||||
testBackend(std::string const& type, std::uint64_t const seedValue, int numObjsToTest = 2000)
|
||||
{
|
||||
DummyScheduler scheduler;
|
||||
|
||||
testcase("Backend type=" + type);
|
||||
|
||||
Section params;
|
||||
beast::TempDir const tempDir;
|
||||
params.set(Keys::kType, type);
|
||||
params.set(Keys::kPath, tempDir.path());
|
||||
|
||||
beast::xor_shift_engine rng(seedValue);
|
||||
|
||||
// Create a batch
|
||||
auto batch = createPredictableBatch(numObjsToTest, rng());
|
||||
|
||||
using beast::Severity;
|
||||
test::SuiteJournal journal("Backend_test", *this);
|
||||
|
||||
{
|
||||
// Open the backend
|
||||
std::unique_ptr<Backend> backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
backend->open();
|
||||
|
||||
// Write the batch
|
||||
storeBatch(*backend, batch);
|
||||
|
||||
{
|
||||
// Read it back in
|
||||
Batch copy;
|
||||
fetchCopyOfBatch(*backend, ©, batch);
|
||||
BEAST_EXPECT(areBatchesEqual(batch, copy));
|
||||
}
|
||||
|
||||
{
|
||||
// Reorder and read the copy again
|
||||
std::shuffle(batch.begin(), batch.end(), rng);
|
||||
Batch copy;
|
||||
fetchCopyOfBatch(*backend, ©, batch);
|
||||
BEAST_EXPECT(areBatchesEqual(batch, copy));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Re-open the backend
|
||||
std::unique_ptr<Backend> backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
backend->open();
|
||||
|
||||
// Read it back in
|
||||
Batch copy;
|
||||
fetchCopyOfBatch(*backend, ©, batch);
|
||||
// Canonicalize the source and destination batches
|
||||
std::ranges::sort(batch, LessThan{});
|
||||
std::ranges::sort(copy, LessThan{});
|
||||
BEAST_EXPECT(areBatchesEqual(batch, copy));
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
std::uint64_t const seedValue = 50;
|
||||
|
||||
testBackend("nudb", seedValue);
|
||||
|
||||
#if XRPL_ROCKSDB_AVAILABLE
|
||||
testBackend("rocksdb", seedValue);
|
||||
#endif
|
||||
|
||||
#ifdef XRPL_ENABLE_SQLITE_BACKEND_TESTS
|
||||
testBackend("sqlite", seedValue);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Backend, nodestore, xrpl);
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
@@ -1,73 +0,0 @@
|
||||
#include <test/nodestore/TestBase.h>
|
||||
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/nodestore/NodeObject.h>
|
||||
#include <xrpl/nodestore/detail/DecodedBlob.h>
|
||||
#include <xrpl/nodestore/detail/EncodedBlob.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
|
||||
// Tests predictable batches, and NodeObject blob encoding
|
||||
//
|
||||
class NodeStoreBasic_test : public TestBase
|
||||
{
|
||||
public:
|
||||
// Make sure predictable object generation works!
|
||||
void
|
||||
testBatches(std::uint64_t const seedValue)
|
||||
{
|
||||
testcase("batch");
|
||||
|
||||
auto batch1 = createPredictableBatch(kNumObjectsToTest, seedValue);
|
||||
|
||||
auto batch2 = createPredictableBatch(kNumObjectsToTest, seedValue);
|
||||
|
||||
BEAST_EXPECT(areBatchesEqual(batch1, batch2));
|
||||
|
||||
auto batch3 = createPredictableBatch(kNumObjectsToTest, seedValue + 1);
|
||||
|
||||
BEAST_EXPECT(!areBatchesEqual(batch1, batch3));
|
||||
}
|
||||
|
||||
// Checks encoding/decoding blobs
|
||||
void
|
||||
testBlobs(std::uint64_t const seedValue)
|
||||
{
|
||||
testcase("encoding");
|
||||
|
||||
auto batch = createPredictableBatch(kNumObjectsToTest, seedValue);
|
||||
|
||||
for (auto const& expected : batch)
|
||||
{
|
||||
EncodedBlob const encoded(expected);
|
||||
|
||||
DecodedBlob decoded(encoded.getKey(), encoded.getData(), encoded.getSize());
|
||||
|
||||
BEAST_EXPECT(decoded.wasOk());
|
||||
|
||||
if (decoded.wasOk())
|
||||
{
|
||||
std::shared_ptr<NodeObject> const object(decoded.createObject());
|
||||
|
||||
BEAST_EXPECT(isSame(expected, object));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
std::uint64_t const seedValue = 50;
|
||||
|
||||
testBatches(seedValue);
|
||||
|
||||
testBlobs(seedValue);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(NodeStoreBasic, nodestore, xrpl);
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
@@ -1,44 +1,21 @@
|
||||
#include <test/jtx/CheckMessageLogs.h>
|
||||
#include <test/jtx/Env.h>
|
||||
#include <test/jtx/envconfig.h>
|
||||
#include <test/nodestore/TestBase.h>
|
||||
#include <test/unit_test/SuiteJournal.h>
|
||||
|
||||
#include <xrpld/core/Config.h>
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/temp_dir.h>
|
||||
#include <xrpl/beast/xor_shift_engine.h>
|
||||
#include <xrpl/config/BasicConfig.h>
|
||||
#include <xrpl/config/Constants.h>
|
||||
#include <xrpl/nodestore/Database.h>
|
||||
#include <xrpl/nodestore/DummyScheduler.h>
|
||||
#include <xrpl/nodestore/Manager.h>
|
||||
#include <xrpl/nodestore/Types.h>
|
||||
#include <xrpl/protocol/SystemParameters.h>
|
||||
#include <xrpl/rdb/DatabaseCon.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
namespace xrpl::node_store {
|
||||
|
||||
class Database_test : public TestBase
|
||||
class DatabaseConfig_test : public beast::unit_test::Suite
|
||||
{
|
||||
test::SuiteJournal journal_;
|
||||
|
||||
public:
|
||||
Database_test() : journal_("Database_test", *this)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
testConfig()
|
||||
{
|
||||
@@ -73,8 +50,8 @@ public:
|
||||
Env env = [&]() {
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kSafetyLevel, "high");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("safety_level", "high");
|
||||
}
|
||||
p->ledgerHistory = 100'000'000;
|
||||
|
||||
@@ -102,8 +79,8 @@ public:
|
||||
Env env = [&]() {
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kSafetyLevel, "low");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("safety_level", "low");
|
||||
}
|
||||
p->ledgerHistory = 100'000'000;
|
||||
|
||||
@@ -131,10 +108,10 @@ public:
|
||||
Env env = [&]() {
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kJournalMode, "off");
|
||||
section.set(Keys::kSynchronous, "extra");
|
||||
section.set(Keys::kTempStore, "default");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("journal_mode", "off");
|
||||
section.set("synchronous", "extra");
|
||||
section.set("temp_store", "default");
|
||||
}
|
||||
|
||||
return Env(
|
||||
@@ -145,7 +122,7 @@ public:
|
||||
}();
|
||||
|
||||
// No warning, even though higher risk settings were used because
|
||||
// LEDGER_HISTORY is small
|
||||
// ledgerHistory is small
|
||||
BEAST_EXPECT(!found);
|
||||
auto const s = setupDatabaseCon(env.app().config());
|
||||
if (BEAST_EXPECT(s.globalPragma->size() == 3))
|
||||
@@ -163,10 +140,10 @@ public:
|
||||
Env env = [&]() {
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kJournalMode, "off");
|
||||
section.set(Keys::kSynchronous, "extra");
|
||||
section.set(Keys::kTempStore, "default");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("journal_mode", "off");
|
||||
section.set("synchronous", "extra");
|
||||
section.set("temp_store", "default");
|
||||
}
|
||||
p->ledgerHistory = 50'000'000;
|
||||
|
||||
@@ -178,7 +155,7 @@ public:
|
||||
}();
|
||||
|
||||
// No warning, even though higher risk settings were used because
|
||||
// LEDGER_HISTORY is small
|
||||
// ledgerHistory is small
|
||||
BEAST_EXPECT(found);
|
||||
auto const s = setupDatabaseCon(env.app().config());
|
||||
if (BEAST_EXPECT(s.globalPragma->size() == 3))
|
||||
@@ -199,11 +176,11 @@ public:
|
||||
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kSafetyLevel, "low");
|
||||
section.set(Keys::kJournalMode, "off");
|
||||
section.set(Keys::kSynchronous, "extra");
|
||||
section.set(Keys::kTempStore, "default");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("safety_level", "low");
|
||||
section.set("journal_mode", "off");
|
||||
section.set("synchronous", "extra");
|
||||
section.set("temp_store", "default");
|
||||
}
|
||||
|
||||
try
|
||||
@@ -230,9 +207,9 @@ public:
|
||||
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kSafetyLevel, "high");
|
||||
section.set(Keys::kJournalMode, "off");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("safety_level", "high");
|
||||
section.set("journal_mode", "off");
|
||||
}
|
||||
|
||||
try
|
||||
@@ -259,9 +236,9 @@ public:
|
||||
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kSafetyLevel, "low");
|
||||
section.set(Keys::kSynchronous, "extra");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("safety_level", "low");
|
||||
section.set("synchronous", "extra");
|
||||
}
|
||||
|
||||
try
|
||||
@@ -288,9 +265,9 @@ public:
|
||||
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kSafetyLevel, "high");
|
||||
section.set(Keys::kTempStore, "default");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("safety_level", "high");
|
||||
section.set("temp_store", "default");
|
||||
}
|
||||
|
||||
try
|
||||
@@ -317,8 +294,8 @@ public:
|
||||
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kSafetyLevel, "slow");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("safety_level", "slow");
|
||||
}
|
||||
|
||||
try
|
||||
@@ -345,8 +322,8 @@ public:
|
||||
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kJournalMode, "fast");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("journal_mode", "fast");
|
||||
}
|
||||
|
||||
try
|
||||
@@ -373,8 +350,8 @@ public:
|
||||
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kSynchronous, "instant");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("synchronous", "instant");
|
||||
}
|
||||
|
||||
try
|
||||
@@ -401,8 +378,8 @@ public:
|
||||
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kTempStore, "network");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("temp_store", "network");
|
||||
}
|
||||
|
||||
try
|
||||
@@ -436,9 +413,9 @@ public:
|
||||
Env env = [&]() {
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kPageSize, "512");
|
||||
section.set(Keys::kJournalSizeLimit, "2582080");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("page_size", "512");
|
||||
section.set("journal_size_limit", "2582080");
|
||||
}
|
||||
return Env(*this, std::move(p));
|
||||
}();
|
||||
@@ -457,8 +434,8 @@ public:
|
||||
bool found = false;
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kPageSize, "256");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("page_size", "256");
|
||||
}
|
||||
try
|
||||
{
|
||||
@@ -480,8 +457,8 @@ public:
|
||||
bool found = false;
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kPageSize, "131072");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("page_size", "131072");
|
||||
}
|
||||
try
|
||||
{
|
||||
@@ -503,8 +480,8 @@ public:
|
||||
bool found = false;
|
||||
auto p = test::jtx::envconfig();
|
||||
{
|
||||
auto& section = p->section(Sections::kSqlite);
|
||||
section.set(Keys::kPageSize, "513");
|
||||
auto& section = p->section("sqlite");
|
||||
section.set("page_size", "513");
|
||||
}
|
||||
try
|
||||
{
|
||||
@@ -522,208 +499,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
testImport(
|
||||
std::string const& destBackendType,
|
||||
std::string const& srcBackendType,
|
||||
std::int64_t seedValue)
|
||||
{
|
||||
DummyScheduler scheduler;
|
||||
|
||||
beast::TempDir const nodeDb;
|
||||
Section srcParams;
|
||||
srcParams.set(Keys::kType, srcBackendType);
|
||||
srcParams.set(Keys::kPath, nodeDb.path());
|
||||
|
||||
// Create a batch
|
||||
auto batch = createPredictableBatch(kNumObjectsToTest, seedValue);
|
||||
|
||||
// Write to source db
|
||||
{
|
||||
std::unique_ptr<Database> src =
|
||||
Manager::instance().makeDatabase(megabytes(4), scheduler, 2, srcParams, journal_);
|
||||
storeBatch(*src, batch);
|
||||
}
|
||||
|
||||
Batch copy;
|
||||
|
||||
{
|
||||
// Re-open the db
|
||||
std::unique_ptr<Database> src =
|
||||
Manager::instance().makeDatabase(megabytes(4), scheduler, 2, srcParams, journal_);
|
||||
|
||||
// Set up the destination database
|
||||
beast::TempDir const destDb;
|
||||
Section destParams;
|
||||
destParams.set(Keys::kType, destBackendType);
|
||||
destParams.set(Keys::kPath, destDb.path());
|
||||
|
||||
std::unique_ptr<Database> dest =
|
||||
Manager::instance().makeDatabase(megabytes(4), scheduler, 2, destParams, journal_);
|
||||
|
||||
testcase("import into '" + destBackendType + "' from '" + srcBackendType + "'");
|
||||
|
||||
// Do the import
|
||||
dest->importDatabase(*src);
|
||||
|
||||
// Get the results of the import
|
||||
fetchCopyOfBatch(*dest, ©, batch);
|
||||
}
|
||||
|
||||
// Canonicalize the source and destination batches
|
||||
std::ranges::sort(batch, LessThan{});
|
||||
std::ranges::sort(copy, LessThan{});
|
||||
BEAST_EXPECT(areBatchesEqual(batch, copy));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
testNodeStore(
|
||||
std::string const& type,
|
||||
bool const testPersistence,
|
||||
std::int64_t const seedValue,
|
||||
int numObjsToTest = 2000)
|
||||
{
|
||||
DummyScheduler scheduler;
|
||||
|
||||
std::string const s = "NodeStore backend '" + type + "'";
|
||||
|
||||
testcase(s);
|
||||
|
||||
beast::TempDir const nodeDb;
|
||||
Section nodeParams;
|
||||
nodeParams.set(Keys::kType, type);
|
||||
nodeParams.set(Keys::kPath, nodeDb.path());
|
||||
|
||||
beast::xor_shift_engine rng(seedValue);
|
||||
|
||||
// Create a batch
|
||||
auto batch = createPredictableBatch(numObjsToTest, rng());
|
||||
|
||||
{
|
||||
// Open the database
|
||||
std::unique_ptr<Database> db =
|
||||
Manager::instance().makeDatabase(megabytes(4), scheduler, 2, nodeParams, journal_);
|
||||
|
||||
// Write the batch
|
||||
storeBatch(*db, batch);
|
||||
|
||||
{
|
||||
// Read it back in
|
||||
Batch copy;
|
||||
fetchCopyOfBatch(*db, ©, batch);
|
||||
BEAST_EXPECT(areBatchesEqual(batch, copy));
|
||||
}
|
||||
|
||||
{
|
||||
// Reorder and read the copy again
|
||||
std::shuffle(batch.begin(), batch.end(), rng);
|
||||
Batch copy;
|
||||
fetchCopyOfBatch(*db, ©, batch);
|
||||
BEAST_EXPECT(areBatchesEqual(batch, copy));
|
||||
}
|
||||
}
|
||||
|
||||
if (testPersistence)
|
||||
{
|
||||
// Re-open the database without the ephemeral DB
|
||||
std::unique_ptr<Database> db =
|
||||
Manager::instance().makeDatabase(megabytes(4), scheduler, 2, nodeParams, journal_);
|
||||
|
||||
// Read it back in
|
||||
Batch copy;
|
||||
fetchCopyOfBatch(*db, ©, batch);
|
||||
|
||||
// Canonicalize the source and destination batches
|
||||
std::ranges::sort(batch, LessThan{});
|
||||
std::ranges::sort(copy, LessThan{});
|
||||
BEAST_EXPECT(areBatchesEqual(batch, copy));
|
||||
}
|
||||
|
||||
if (type == "memory")
|
||||
{
|
||||
// Verify default earliest ledger sequence
|
||||
{
|
||||
std::unique_ptr<Database> db = Manager::instance().makeDatabase(
|
||||
megabytes(4), scheduler, 2, nodeParams, journal_);
|
||||
BEAST_EXPECT(db->earliestLedgerSeq() == kXrpLedgerEarliestSeq);
|
||||
}
|
||||
|
||||
// Set an invalid earliest ledger sequence
|
||||
try
|
||||
{
|
||||
nodeParams.set(Keys::kEarliestSeq, "0");
|
||||
std::unique_ptr<Database> const db = Manager::instance().makeDatabase(
|
||||
megabytes(4), scheduler, 2, nodeParams, journal_);
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(std::strcmp(e.what(), "Invalid earliest_seq") == 0);
|
||||
}
|
||||
|
||||
{
|
||||
// Set a valid earliest ledger sequence
|
||||
nodeParams.set(Keys::kEarliestSeq, "1");
|
||||
std::unique_ptr<Database> db = Manager::instance().makeDatabase(
|
||||
megabytes(4), scheduler, 2, nodeParams, journal_);
|
||||
|
||||
// Verify database uses the earliest ledger sequence setting
|
||||
BEAST_EXPECT(db->earliestLedgerSeq() == 1);
|
||||
}
|
||||
|
||||
// Create another database that attempts to set the value again
|
||||
try
|
||||
{
|
||||
// Set to default earliest ledger sequence
|
||||
nodeParams.set(Keys::kEarliestSeq, std::to_string(kXrpLedgerEarliestSeq));
|
||||
std::unique_ptr<Database> const db2 = Manager::instance().makeDatabase(
|
||||
megabytes(4), scheduler, 2, nodeParams, journal_);
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
BEAST_EXPECT(std::strcmp(e.what(), "earliest_seq set more than once") == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
std::int64_t const seedValue = 50;
|
||||
|
||||
testConfig();
|
||||
|
||||
testNodeStore("memory", false, seedValue);
|
||||
|
||||
// Persistent backend tests
|
||||
{
|
||||
testNodeStore("nudb", true, seedValue);
|
||||
|
||||
#if XRPL_ROCKSDB_AVAILABLE
|
||||
testNodeStore("rocksdb", true, seedValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Import tests
|
||||
{
|
||||
testImport("nudb", "nudb", seedValue);
|
||||
|
||||
#if XRPL_ROCKSDB_AVAILABLE
|
||||
testImport("rocksdb", "rocksdb", seedValue);
|
||||
#endif
|
||||
|
||||
#if XRPL_ENABLE_SQLITE_BACKEND_TESTS
|
||||
testImport("sqlite", "sqlite", seedValue);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(Database, nodestore, xrpl);
|
||||
BEAST_DEFINE_TESTSUITE(DatabaseConfig, nodestore, xrpl);
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
} // namespace xrpl::node_store
|
||||
@@ -1,443 +0,0 @@
|
||||
#include <test/nodestore/TestBase.h>
|
||||
#include <test/unit_test/SuiteJournal.h>
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
#include <xrpl/basics/Number.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/temp_dir.h>
|
||||
#include <xrpl/config/BasicConfig.h>
|
||||
#include <xrpl/config/Constants.h>
|
||||
#include <xrpl/nodestore/DummyScheduler.h>
|
||||
#include <xrpl/nodestore/Manager.h>
|
||||
#include <xrpl/nodestore/Types.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
|
||||
class NuDBFactory_test : public TestBase
|
||||
{
|
||||
private:
|
||||
// Helper function to create a Section with specified parameters
|
||||
static Section
|
||||
createSection(std::string const& path, std::string const& blockSize = "")
|
||||
{
|
||||
Section params;
|
||||
params.set(Keys::kType, "nudb");
|
||||
params.set(Keys::kPath, path);
|
||||
if (!blockSize.empty())
|
||||
params.set(Keys::kNudbBlockSize, blockSize);
|
||||
return params;
|
||||
}
|
||||
|
||||
// Helper function to create a backend and test basic functionality
|
||||
bool
|
||||
testBackendFunctionality(Section const& params, std::size_t expectedBlocksize)
|
||||
{
|
||||
try
|
||||
{
|
||||
DummyScheduler scheduler;
|
||||
test::SuiteJournal journal("NuDBFactory_test", *this);
|
||||
|
||||
auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
|
||||
if (!BEAST_EXPECT(backend))
|
||||
return false;
|
||||
|
||||
if (!BEAST_EXPECT(backend->getBlockSize() == expectedBlocksize))
|
||||
return false;
|
||||
|
||||
backend->open();
|
||||
|
||||
if (!BEAST_EXPECT(backend->isOpen()))
|
||||
return false;
|
||||
|
||||
// Test basic store/fetch functionality
|
||||
auto batch = createPredictableBatch(10, 12345);
|
||||
storeBatch(*backend, batch);
|
||||
|
||||
Batch copy;
|
||||
fetchCopyOfBatch(*backend, ©, batch);
|
||||
|
||||
backend->close();
|
||||
|
||||
return areBatchesEqual(batch, copy);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to test log messages
|
||||
void
|
||||
testLogMessage(Section const& params, beast::Severity level, std::string const& expectedMessage)
|
||||
{
|
||||
test::StreamSink sink(level);
|
||||
beast::Journal const journal(sink);
|
||||
|
||||
DummyScheduler scheduler;
|
||||
auto backend = Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
|
||||
std::string const logOutput = sink.messages().str();
|
||||
BEAST_EXPECT(logOutput.contains(expectedMessage));
|
||||
}
|
||||
|
||||
// Helper function to test power of two validation
|
||||
void
|
||||
testPowerOfTwoValidation(std::string const& size, bool shouldWork)
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto params = createSection(tempDir.path(), size);
|
||||
|
||||
test::StreamSink sink(beast::Severity::Warning);
|
||||
beast::Journal const journal(sink);
|
||||
|
||||
DummyScheduler scheduler;
|
||||
auto backend = Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
|
||||
std::string const logOutput = sink.messages().str();
|
||||
bool const hasWarning = logOutput.contains("Invalid nudb_block_size");
|
||||
|
||||
BEAST_EXPECT(hasWarning == !shouldWork);
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
testDefaultBlockSize()
|
||||
{
|
||||
testcase("Default block size (no nudb_block_size specified)");
|
||||
|
||||
beast::TempDir const tempDir;
|
||||
auto params = createSection(tempDir.path());
|
||||
|
||||
// Should work with default 4096 block size
|
||||
BEAST_EXPECT(testBackendFunctionality(params, 4096));
|
||||
}
|
||||
|
||||
void
|
||||
testValidBlockSizes()
|
||||
{
|
||||
testcase("Valid block sizes");
|
||||
|
||||
std::vector<std::size_t> const validSizes = {4096, 8192, 16384, 32768};
|
||||
|
||||
for (auto const& size : validSizes)
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto params = createSection(tempDir.path(), to_string(size));
|
||||
|
||||
BEAST_EXPECT(testBackendFunctionality(params, size));
|
||||
}
|
||||
// Empty value is ignored by the config parser, so uses the
|
||||
// default
|
||||
beast::TempDir const tempDir;
|
||||
auto params = createSection(tempDir.path(), "");
|
||||
|
||||
BEAST_EXPECT(testBackendFunctionality(params, 4096));
|
||||
}
|
||||
|
||||
void
|
||||
testInvalidBlockSizes()
|
||||
{
|
||||
testcase("Invalid block sizes");
|
||||
|
||||
std::vector<std::string> const invalidSizes = {
|
||||
"2048", // Too small
|
||||
"1024", // Too small
|
||||
"65536", // Too large
|
||||
"131072", // Too large
|
||||
"5000", // Not power of 2
|
||||
"6000", // Not power of 2
|
||||
"10000", // Not power of 2
|
||||
"0", // Zero
|
||||
"-1", // Negative
|
||||
"abc", // Non-numeric
|
||||
"4k", // Invalid format
|
||||
"4096.5" // Decimal
|
||||
};
|
||||
|
||||
for (auto const& size : invalidSizes)
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto params = createSection(tempDir.path(), size);
|
||||
|
||||
// Fails
|
||||
BEAST_EXPECT(!testBackendFunctionality(params, 4096));
|
||||
}
|
||||
|
||||
// Test whitespace cases separately since lexical_cast may handle them
|
||||
std::vector<std::string> const whitespaceInvalidSizes = {
|
||||
"4096 ", // Trailing space - might be handled by lexical_cast
|
||||
" 4096" // Leading space - might be handled by lexical_cast
|
||||
};
|
||||
|
||||
for (auto const& size : whitespaceInvalidSizes)
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto params = createSection(tempDir.path(), size);
|
||||
|
||||
// Fails
|
||||
BEAST_EXPECT(!testBackendFunctionality(params, 4096));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testLogMessages()
|
||||
{
|
||||
testcase("Log message verification");
|
||||
|
||||
// Test valid custom block size logging
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto params = createSection(tempDir.path(), "8192");
|
||||
|
||||
testLogMessage(params, beast::Severity::Info, "Using custom NuDB block size: 8192");
|
||||
}
|
||||
|
||||
// Test invalid block size failure
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto params = createSection(tempDir.path(), "5000");
|
||||
|
||||
test::StreamSink sink(beast::Severity::Warning);
|
||||
beast::Journal const journal(sink);
|
||||
|
||||
DummyScheduler scheduler;
|
||||
try
|
||||
{
|
||||
auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
fail();
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
std::string const logOutput{e.what()};
|
||||
BEAST_EXPECT(logOutput.contains("Invalid nudb_block_size: 5000"));
|
||||
BEAST_EXPECT(logOutput.contains("Must be power of 2 between 4096 and 32768"));
|
||||
}
|
||||
}
|
||||
|
||||
// Test non-numeric value failure
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto params = createSection(tempDir.path(), "invalid");
|
||||
|
||||
test::StreamSink sink(beast::Severity::Warning);
|
||||
beast::Journal const journal(sink);
|
||||
|
||||
DummyScheduler scheduler;
|
||||
try
|
||||
{
|
||||
auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
|
||||
fail();
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
std::string const logOutput{e.what()};
|
||||
BEAST_EXPECT(logOutput.contains("Invalid nudb_block_size value: invalid"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testPowerOfTwoValidation()
|
||||
{
|
||||
testcase("Power of 2 validation logic");
|
||||
|
||||
// Test edge cases around valid range
|
||||
std::vector<std::pair<std::string, bool>> const testCases = {
|
||||
{"4095", false}, // Just below minimum
|
||||
{"4096", true}, // Minimum valid
|
||||
{"4097", false}, // Just above minimum, not power of 2
|
||||
{"8192", true}, // Valid power of 2
|
||||
{"8193", false}, // Just above valid power of 2
|
||||
{"16384", true}, // Valid power of 2
|
||||
{"32768", true}, // Maximum valid
|
||||
{"32769", false}, // Just above maximum
|
||||
{"65536", false} // Power of 2 but too large
|
||||
};
|
||||
|
||||
for (auto const& [size, shouldWork] : testCases)
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto params = createSection(tempDir.path(), size);
|
||||
|
||||
// We test the validation logic by catching exceptions for invalid
|
||||
// values
|
||||
test::StreamSink sink(beast::Severity::Warning);
|
||||
beast::Journal const journal(sink);
|
||||
|
||||
DummyScheduler scheduler;
|
||||
try
|
||||
{
|
||||
auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
BEAST_EXPECT(shouldWork);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
std::string const logOutput{e.what()};
|
||||
BEAST_EXPECT(logOutput.contains("Invalid nudb_block_size"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testBothConstructorVariants()
|
||||
{
|
||||
testcase("Both constructor variants work with custom block size");
|
||||
|
||||
beast::TempDir const tempDir;
|
||||
auto params = createSection(tempDir.path(), "16384");
|
||||
|
||||
DummyScheduler scheduler;
|
||||
test::SuiteJournal journal("NuDBFactory_test", *this);
|
||||
|
||||
// Test first constructor (without nudb::context)
|
||||
{
|
||||
auto backend1 =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
BEAST_EXPECT(backend1 != nullptr);
|
||||
BEAST_EXPECT(testBackendFunctionality(params, 16384));
|
||||
}
|
||||
|
||||
// Test second constructor (with nudb::context)
|
||||
// Note: This would require access to nudb::context, which might not be
|
||||
// easily testable without more complex setup. For now, we test that
|
||||
// the factory can create backends with the first constructor.
|
||||
}
|
||||
|
||||
void
|
||||
testConfigurationParsing()
|
||||
{
|
||||
testcase("Configuration parsing edge cases");
|
||||
|
||||
// Test that whitespace is handled correctly
|
||||
std::vector<std::string> const validFormats = {
|
||||
"8192" // Basic valid format
|
||||
};
|
||||
|
||||
// Test whitespace handling separately since lexical_cast behavior may
|
||||
// vary
|
||||
std::vector<std::string> const whitespaceFormats = {
|
||||
" 8192", // Leading space - may or may not be handled by
|
||||
// lexical_cast
|
||||
"8192 " // Trailing space - may or may not be handled by
|
||||
// lexical_cast
|
||||
};
|
||||
|
||||
// Test basic valid format
|
||||
for (auto const& format : validFormats)
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto params = createSection(tempDir.path(), format);
|
||||
|
||||
test::StreamSink sink(beast::Severity::Info);
|
||||
beast::Journal const journal(sink);
|
||||
|
||||
DummyScheduler scheduler;
|
||||
auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
|
||||
// Should log success message for valid values
|
||||
std::string const logOutput = sink.messages().str();
|
||||
bool const hasSuccessMessage = logOutput.contains("Using custom NuDB block size");
|
||||
BEAST_EXPECT(hasSuccessMessage);
|
||||
}
|
||||
|
||||
// Test whitespace formats - these should work if lexical_cast handles
|
||||
// them
|
||||
for (auto const& format : whitespaceFormats)
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto params = createSection(tempDir.path(), format);
|
||||
|
||||
// Use a lower threshold to capture both info and warning messages
|
||||
test::StreamSink sink(beast::Severity::Debug);
|
||||
beast::Journal const journal(sink);
|
||||
|
||||
DummyScheduler scheduler;
|
||||
try
|
||||
{
|
||||
auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
fail();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Fails
|
||||
BEAST_EXPECT(!testBackendFunctionality(params, 8192));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testDataPersistence()
|
||||
{
|
||||
testcase("Data persistence with different block sizes");
|
||||
|
||||
std::vector<std::string> const blockSizes = {"4096", "8192", "16384", "32768"};
|
||||
|
||||
for (auto const& size : blockSizes)
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto params = createSection(tempDir.path(), size);
|
||||
|
||||
DummyScheduler scheduler;
|
||||
test::SuiteJournal journal("NuDBFactory_test", *this);
|
||||
|
||||
// Create test data
|
||||
auto batch = createPredictableBatch(50, 54321);
|
||||
|
||||
// Store data
|
||||
{
|
||||
auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
backend->open();
|
||||
storeBatch(*backend, batch);
|
||||
backend->close();
|
||||
}
|
||||
|
||||
// Retrieve data in new backend instance
|
||||
{
|
||||
auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
backend->open();
|
||||
|
||||
Batch copy;
|
||||
fetchCopyOfBatch(*backend, ©, batch);
|
||||
|
||||
BEAST_EXPECT(areBatchesEqual(batch, copy));
|
||||
backend->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testDefaultBlockSize();
|
||||
testValidBlockSizes();
|
||||
testInvalidBlockSizes();
|
||||
testLogMessages();
|
||||
testPowerOfTwoValidation();
|
||||
testBothConstructorVariants();
|
||||
testConfigurationParsing();
|
||||
testDataPersistence();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(NuDBFactory, xrpl_core, xrpl);
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
@@ -1,202 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/random.h>
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/beast/utility/rngfill.h>
|
||||
#include <xrpl/beast/xor_shift_engine.h>
|
||||
#include <xrpl/nodestore/Backend.h>
|
||||
#include <xrpl/nodestore/Database.h>
|
||||
#include <xrpl/nodestore/NodeObject.h>
|
||||
#include <xrpl/nodestore/Types.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl::NodeStore {
|
||||
|
||||
/**
|
||||
* Binary function that satisfies the strict-weak-ordering requirement.
|
||||
*
|
||||
* This compares the hashes of both objects and returns true if
|
||||
* the first hash is considered to go before the second.
|
||||
*
|
||||
* @see std::sort
|
||||
*/
|
||||
struct LessThan
|
||||
{
|
||||
bool
|
||||
operator()(std::shared_ptr<NodeObject> const& lhs, std::shared_ptr<NodeObject> const& rhs)
|
||||
const noexcept
|
||||
{
|
||||
return lhs->getHash() < rhs->getHash();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns `true` if objects are identical.
|
||||
*/
|
||||
inline bool
|
||||
isSame(std::shared_ptr<NodeObject> const& lhs, std::shared_ptr<NodeObject> const& rhs)
|
||||
{
|
||||
return (lhs->getType() == rhs->getType()) && (lhs->getHash() == rhs->getHash()) &&
|
||||
(lhs->getData() == rhs->getData());
|
||||
}
|
||||
|
||||
// Some common code for the unit tests
|
||||
//
|
||||
class TestBase : public beast::unit_test::Suite
|
||||
{
|
||||
public:
|
||||
// Tunable parameters
|
||||
//
|
||||
static std::size_t const kMinPayloadBytes = 1;
|
||||
static std::size_t const kMaxPayloadBytes = 2000;
|
||||
static int const kNumObjectsToTest = 2000;
|
||||
|
||||
public:
|
||||
// Create a predictable batch of objects
|
||||
static Batch
|
||||
createPredictableBatch(int numObjects, std::uint64_t seed)
|
||||
{
|
||||
Batch batch;
|
||||
batch.reserve(numObjects);
|
||||
|
||||
beast::xor_shift_engine rng(seed);
|
||||
|
||||
for (int i = 0; i < numObjects; ++i)
|
||||
{
|
||||
NodeObjectType const type = [&] {
|
||||
switch (randInt(rng, 3))
|
||||
{
|
||||
case 0:
|
||||
return NodeObjectType::Ledger;
|
||||
case 1:
|
||||
return NodeObjectType::AccountNode;
|
||||
case 2:
|
||||
return NodeObjectType::TransactionNode;
|
||||
case 3:
|
||||
default:
|
||||
return NodeObjectType::Unknown;
|
||||
}
|
||||
}();
|
||||
|
||||
uint256 hash;
|
||||
beast::rngfill(hash.begin(), hash.size(), rng);
|
||||
|
||||
Blob blob(randInt(rng, kMinPayloadBytes, kMaxPayloadBytes));
|
||||
beast::rngfill(blob.data(), blob.size(), rng);
|
||||
|
||||
batch.push_back(NodeObject::createObject(type, std::move(blob), hash));
|
||||
}
|
||||
|
||||
return batch;
|
||||
}
|
||||
|
||||
// Compare two batches for equality
|
||||
static bool
|
||||
areBatchesEqual(Batch const& lhs, Batch const& rhs)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (lhs.size() == rhs.size())
|
||||
{
|
||||
for (int i = 0; i < lhs.size(); ++i)
|
||||
{
|
||||
if (!isSame(lhs[i], rhs[i]))
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Store a batch in a backend
|
||||
static void
|
||||
storeBatch(Backend& backend, Batch const& batch)
|
||||
{
|
||||
for (auto const& object : batch)
|
||||
{
|
||||
backend.store(object);
|
||||
}
|
||||
}
|
||||
|
||||
// Get a copy of a batch in a backend
|
||||
void
|
||||
fetchCopyOfBatch(Backend& backend, Batch* pCopy, Batch const& batch)
|
||||
{
|
||||
pCopy->clear();
|
||||
pCopy->reserve(batch.size());
|
||||
|
||||
for (auto const& expected : batch)
|
||||
{
|
||||
std::shared_ptr<NodeObject> object;
|
||||
|
||||
Status const status = backend.fetch(expected->getHash(), &object);
|
||||
|
||||
BEAST_EXPECT(status == Status::Ok);
|
||||
|
||||
if (status == Status::Ok)
|
||||
{
|
||||
BEAST_EXPECT(object != nullptr);
|
||||
|
||||
pCopy->push_back(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fetchMissing(Backend& backend, Batch const& batch)
|
||||
{
|
||||
for (auto const& expected : batch)
|
||||
{
|
||||
std::shared_ptr<NodeObject> object;
|
||||
|
||||
Status const status = backend.fetch(expected->getHash(), &object);
|
||||
|
||||
BEAST_EXPECT(status == Status::NotFound);
|
||||
}
|
||||
}
|
||||
|
||||
// Store all objects in a batch
|
||||
static void
|
||||
storeBatch(Database& db, Batch const& batch)
|
||||
{
|
||||
for (auto const& object : batch)
|
||||
{
|
||||
Blob data(object->getData());
|
||||
|
||||
db.store(object->getType(), std::move(data), object->getHash(), db.earliestLedgerSeq());
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all the hashes in one batch, into another batch.
|
||||
static void
|
||||
fetchCopyOfBatch(Database& db, Batch* pCopy, Batch const& batch)
|
||||
{
|
||||
pCopy->clear();
|
||||
pCopy->reserve(batch.size());
|
||||
|
||||
for (auto const& expected : batch)
|
||||
{
|
||||
std::shared_ptr<NodeObject> const object = db.fetchNodeObject(expected->getHash(), 0);
|
||||
|
||||
if (object != nullptr)
|
||||
pCopy->push_back(object);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl::NodeStore
|
||||
@@ -195,7 +195,7 @@ fmtdur(std::chrono::duration<Period, Rep> const& d)
|
||||
|
||||
} // namespace detail
|
||||
|
||||
namespace NodeStore {
|
||||
namespace node_store {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -552,5 +552,5 @@ BEAST_DEFINE_TESTSUITE_MANUAL(import, nodestore, xrpl);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace NodeStore
|
||||
} // namespace node_store
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
#include <xrpl/beast/unit_test/suite.h>
|
||||
#include <xrpl/nodestore/detail/varint.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::NodeStore::tests {
|
||||
|
||||
class varint_test : public beast::unit_test::Suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
testVarints(std::vector<std::size_t> vv)
|
||||
{
|
||||
testcase("encode, decode");
|
||||
for (auto const v : vv)
|
||||
{
|
||||
std::array<std::uint8_t, varint_traits<std::size_t>::kMax> vi{};
|
||||
auto const n0 = writeVarint(vi.data(), v);
|
||||
expect(n0 > 0, "write error");
|
||||
expect(n0 == sizeVarint(v), "size error");
|
||||
std::size_t v1 = 0;
|
||||
auto const n1 = readVarint(vi.data(), n0, v1);
|
||||
expect(n1 == n0, "read error");
|
||||
expect(v == v1, "wrong value");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testVarints(
|
||||
{0,
|
||||
1,
|
||||
2,
|
||||
126,
|
||||
127,
|
||||
128,
|
||||
253,
|
||||
254,
|
||||
255,
|
||||
16127,
|
||||
16128,
|
||||
16129,
|
||||
0xff,
|
||||
0xffff,
|
||||
0xffffffff,
|
||||
0xffffffffffffUL,
|
||||
0xffffffffffffffffUL});
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(varint, nodestore, xrpl);
|
||||
|
||||
} // namespace xrpl::NodeStore::tests
|
||||
@@ -35,6 +35,7 @@ set(test_modules
|
||||
shamap
|
||||
tx
|
||||
protocol_autogen
|
||||
nodestore
|
||||
)
|
||||
if(NOT WIN32)
|
||||
list(APPEND test_modules net)
|
||||
|
||||
50
src/tests/libxrpl/helpers/CaptureSink.h
Normal file
50
src/tests/libxrpl/helpers/CaptureSink.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl::test {
|
||||
|
||||
class CaptureSink : public beast::Journal::Sink
|
||||
{
|
||||
mutable std::mutex mutex_;
|
||||
std::stringstream strm_;
|
||||
|
||||
public:
|
||||
explicit CaptureSink(beast::Severity threshold = beast::Severity::Debug)
|
||||
: Sink{threshold, false}
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
write(beast::Severity level, std::string const& text) override
|
||||
{
|
||||
if (level < threshold())
|
||||
return;
|
||||
writeAlways(level, text);
|
||||
}
|
||||
|
||||
void
|
||||
writeAlways(beast::Severity /*level*/, std::string const& text) override
|
||||
{
|
||||
// Journal sinks may be written to concurrently (e.g. from a backend's background workers),
|
||||
// so serialize access to strm_. write() funnels into writeAlways(), so the lock lives here
|
||||
// only: locking in both would self-deadlock on this non-recursive mutex.
|
||||
std::scoped_lock const lock(mutex_);
|
||||
strm_ << text << '\n';
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string
|
||||
messages() const
|
||||
{
|
||||
// Returns a snapshot of the captured output. Takes the lock so the read is safe even if a
|
||||
// writer is still active.
|
||||
std::scoped_lock const lock(mutex_);
|
||||
return strm_.str();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace xrpl::test
|
||||
@@ -29,11 +29,11 @@ namespace xrpl::test {
|
||||
class TestFamily : public Family
|
||||
{
|
||||
private:
|
||||
std::unique_ptr<NodeStore::Database> db_;
|
||||
std::unique_ptr<node_store::Database> db_;
|
||||
TestStopwatch clock_;
|
||||
std::shared_ptr<FullBelowCache> fbCache_;
|
||||
std::shared_ptr<TreeNodeCache> tnCache_;
|
||||
NodeStore::DummyScheduler scheduler_;
|
||||
node_store::DummyScheduler scheduler_;
|
||||
beast::Journal j_;
|
||||
|
||||
public:
|
||||
@@ -51,16 +51,16 @@ public:
|
||||
Section config;
|
||||
config.set(Keys::kType, "memory");
|
||||
config.set(Keys::kPath, "TestFamily");
|
||||
db_ = NodeStore::Manager::instance().makeDatabase(megabytes(4), scheduler_, 1, config, j);
|
||||
db_ = node_store::Manager::instance().makeDatabase(megabytes(4), scheduler_, 1, config, j);
|
||||
}
|
||||
|
||||
NodeStore::Database&
|
||||
node_store::Database&
|
||||
db() override
|
||||
{
|
||||
return *db_;
|
||||
}
|
||||
|
||||
[[nodiscard]] NodeStore::Database const&
|
||||
[[nodiscard]] node_store::Database const&
|
||||
db() const override
|
||||
{
|
||||
return *db_;
|
||||
|
||||
@@ -220,7 +220,7 @@ public:
|
||||
}
|
||||
|
||||
// Storage services
|
||||
NodeStore::Database&
|
||||
node_store::Database&
|
||||
getNodeStore() override
|
||||
{
|
||||
throw std::logic_error("TestServiceRegistry::getNodeStore() not implemented");
|
||||
|
||||
182
src/tests/libxrpl/nodestore/Backend.cpp
Normal file
182
src/tests/libxrpl/nodestore/Backend.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
#include <xrpl/nodestore/Backend.h>
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/temp_dir.h>
|
||||
#include <xrpl/beast/xor_shift_engine.h>
|
||||
#include <xrpl/config/BasicConfig.h>
|
||||
#include <xrpl/nodestore/DummyScheduler.h>
|
||||
#include <xrpl/nodestore/Manager.h>
|
||||
#include <xrpl/nodestore/NodeObject.h>
|
||||
#include <xrpl/nodestore/Types.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <helpers/TestSink.h>
|
||||
#include <nodestore/TestBase.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::node_store {
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<std::string>
|
||||
backendTypes()
|
||||
{
|
||||
std::vector<std::string> types{"nudb"};
|
||||
#if XRPL_ROCKSDB_AVAILABLE
|
||||
types.emplace_back("rocksdb");
|
||||
#endif
|
||||
#ifdef XRPL_ENABLE_SQLITE_BACKEND_TESTS
|
||||
types.emplace_back("sqlite");
|
||||
#endif
|
||||
return types;
|
||||
}
|
||||
|
||||
// Run work(i) for every i in [0, n) spread across numThreads threads, handing
|
||||
// out indices via a shared atomic counter (mirrors the old Timing_test
|
||||
// parallel-for so the N items are partitioned, not duplicated).
|
||||
template <class Work>
|
||||
void
|
||||
parallelFor(std::size_t n, std::size_t numThreads, Work work)
|
||||
{
|
||||
std::atomic<std::size_t> next{0};
|
||||
auto const runner = [&] {
|
||||
for (std::size_t i = next++; i < n; i = next++)
|
||||
work(i);
|
||||
};
|
||||
|
||||
auto threads = std::views::iota(std::size_t{0}, numThreads) |
|
||||
std::views::transform([&](std::size_t) { return std::thread{runner}; }) |
|
||||
std::ranges::to<std::vector>();
|
||||
|
||||
std::ranges::for_each(threads, &std::thread::join);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class BackendTypeTest : public ::testing::TestWithParam<std::string>
|
||||
{
|
||||
protected:
|
||||
void
|
||||
SetUp() override
|
||||
{
|
||||
params_.set("type", GetParam());
|
||||
params_.set("path", tempDir_.path());
|
||||
|
||||
beast::xor_shift_engine rng(kSeedValue);
|
||||
batch_ = createPredictableBatch(kNumObjects, rng());
|
||||
}
|
||||
|
||||
std::unique_ptr<Backend>
|
||||
makeOpenBackend()
|
||||
{
|
||||
auto backend = Manager::instance().makeBackend(params_, megabytes(4), scheduler_, journal_);
|
||||
backend->open();
|
||||
return backend;
|
||||
}
|
||||
|
||||
DummyScheduler scheduler_;
|
||||
beast::TempDir const tempDir_;
|
||||
beast::Journal const journal_{TestSink::instance()};
|
||||
Section params_;
|
||||
Batch batch_;
|
||||
};
|
||||
|
||||
TEST_P(BackendTypeTest, store_and_fetch)
|
||||
{
|
||||
auto backend = makeOpenBackend();
|
||||
storeBatch(*backend, batch_);
|
||||
|
||||
{
|
||||
SCOPED_TRACE("read in original order");
|
||||
auto const copy = fetchCopyOfBatch(*backend, batch_);
|
||||
EXPECT_EQ(batch_, copy);
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TRACE("read in shuffled order");
|
||||
beast::xor_shift_engine rng(kSeedValue);
|
||||
std::shuffle(batch_.begin(), batch_.end(), rng);
|
||||
auto const copy = fetchCopyOfBatch(*backend, batch_);
|
||||
EXPECT_EQ(batch_, copy);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(BackendTypeTest, persists_after_reopen)
|
||||
{
|
||||
{
|
||||
auto backend = makeOpenBackend();
|
||||
storeBatch(*backend, batch_);
|
||||
}
|
||||
|
||||
// re-open a fresh backend instance over the same path
|
||||
auto backend = makeOpenBackend();
|
||||
auto copy = fetchCopyOfBatch(*backend, batch_);
|
||||
std::ranges::sort(batch_, LessThan{});
|
||||
std::ranges::sort(copy, LessThan{});
|
||||
EXPECT_EQ(batch_, copy);
|
||||
}
|
||||
|
||||
// missing-key path. Replaces the correctness half of Timing_test::doMissing
|
||||
// (and the missing branch of doMixed): every fetch on an empty backend must
|
||||
// report Status::NotFound.
|
||||
TEST_P(BackendTypeTest, fetch_missing)
|
||||
{
|
||||
auto backend = makeOpenBackend();
|
||||
// deliberately do NOT store batch_ — every key must be absent
|
||||
fetchMissing(*backend, batch_);
|
||||
}
|
||||
|
||||
// concurrent store/fetch correctness. Replaces the correctness half of the
|
||||
// multi-threaded Timing_test workloads (which only ran manually, never in CI):
|
||||
// many threads store disjoint objects, then many threads fetch and verify each
|
||||
// round-trips. Doubles as a thread-safety smoke test for the backend.
|
||||
TEST_P(BackendTypeTest, concurrent_store_and_fetch)
|
||||
{
|
||||
// The SQLite backend is not designed for concurrent writers (and the old
|
||||
// Timing_test only exercised nudb/rocksdb under threads).
|
||||
if (GetParam() == "sqlite")
|
||||
GTEST_SKIP() << "sqlite backend is not exercised under concurrency";
|
||||
|
||||
for (auto const numThreads : {4uz, 8uz})
|
||||
{
|
||||
SCOPED_TRACE("threads=" + std::to_string(numThreads));
|
||||
|
||||
auto backend = makeOpenBackend();
|
||||
|
||||
// concurrent stores of disjoint objects
|
||||
parallelFor(batch_.size(), numThreads, [&](std::size_t i) { backend->store(batch_[i]); });
|
||||
|
||||
// concurrent fetches, each verifying its object round-trips. Worker
|
||||
// threads only touch an atomic counter; the EXPECT runs on the main
|
||||
// thread after join to avoid relying on cross-thread assertion support.
|
||||
std::atomic<std::size_t> mismatches{0};
|
||||
parallelFor(batch_.size(), numThreads, [&](std::size_t i) {
|
||||
std::shared_ptr<NodeObject> result;
|
||||
if (backend->fetch(batch_[i]->getHash(), &result) != Status::Ok || !result ||
|
||||
!isSame(result, batch_[i]))
|
||||
{
|
||||
++mismatches;
|
||||
}
|
||||
});
|
||||
EXPECT_EQ(mismatches.load(), 0u);
|
||||
|
||||
backend->close();
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
BackendTypes,
|
||||
BackendTypeTest,
|
||||
::testing::ValuesIn(backendTypes()),
|
||||
[](::testing::TestParamInfo<std::string> const& info) { return info.param; });
|
||||
|
||||
} // namespace xrpl::node_store
|
||||
41
src/tests/libxrpl/nodestore/Basics.cpp
Normal file
41
src/tests/libxrpl/nodestore/Basics.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <xrpl/nodestore/NodeObject.h>
|
||||
#include <xrpl/nodestore/detail/DecodedBlob.h>
|
||||
#include <xrpl/nodestore/detail/EncodedBlob.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <nodestore/TestBase.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl::node_store {
|
||||
|
||||
TEST(NodeStoreBasics, predictable_batches)
|
||||
{
|
||||
auto const batch1 = createPredictableBatch(kNumObjectsToTest, kSeedValue);
|
||||
auto const batch2 = createPredictableBatch(kNumObjectsToTest, kSeedValue);
|
||||
EXPECT_EQ(batch1, batch2);
|
||||
|
||||
auto const batch3 = createPredictableBatch(kNumObjectsToTest, kSeedValue + 1);
|
||||
EXPECT_NE(batch1, batch3);
|
||||
}
|
||||
|
||||
TEST(NodeStoreBasics, blob_encoding)
|
||||
{
|
||||
auto const batch = createPredictableBatch(kNumObjectsToTest, kSeedValue);
|
||||
for (std::size_t i = 0; i < batch.size(); ++i)
|
||||
{
|
||||
SCOPED_TRACE("blob index=" + std::to_string(i));
|
||||
EncodedBlob const encoded(batch[i]);
|
||||
DecodedBlob decoded(encoded.getKey(), encoded.getData(), encoded.getSize());
|
||||
EXPECT_TRUE(decoded.wasOk());
|
||||
if (decoded.wasOk())
|
||||
{
|
||||
std::shared_ptr<NodeObject> const object(decoded.createObject());
|
||||
EXPECT_TRUE(isSame(batch[i], object));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xrpl::node_store
|
||||
248
src/tests/libxrpl/nodestore/Database.cpp
Normal file
248
src/tests/libxrpl/nodestore/Database.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
#include <xrpl/nodestore/Database.h>
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/temp_dir.h>
|
||||
#include <xrpl/beast/xor_shift_engine.h>
|
||||
#include <xrpl/config/BasicConfig.h>
|
||||
#include <xrpl/nodestore/DummyScheduler.h>
|
||||
#include <xrpl/nodestore/Manager.h>
|
||||
#include <xrpl/nodestore/Types.h>
|
||||
#include <xrpl/protocol/SystemParameters.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <helpers/TestSink.h>
|
||||
#include <nodestore/TestBase.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::node_store {
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<std::string>
|
||||
allBackends()
|
||||
{
|
||||
std::vector<std::string> types{"memory", "nudb"};
|
||||
#if XRPL_ROCKSDB_AVAILABLE
|
||||
types.emplace_back("rocksdb");
|
||||
#endif
|
||||
return types;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
persistentBackends()
|
||||
{
|
||||
std::vector<std::string> types{"nudb"};
|
||||
#if XRPL_ROCKSDB_AVAILABLE
|
||||
types.emplace_back("rocksdb");
|
||||
#endif
|
||||
return types;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
importBackends()
|
||||
{
|
||||
std::vector<std::string> types{"nudb"};
|
||||
#if XRPL_ROCKSDB_AVAILABLE
|
||||
types.emplace_back("rocksdb");
|
||||
#endif
|
||||
#ifdef XRPL_ENABLE_SQLITE_BACKEND_TESTS
|
||||
types.emplace_back("sqlite");
|
||||
#endif
|
||||
return types;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Shared setup for the parameterized Database tests: builds the node params,
|
||||
// journal and a predictable batch per test, mirroring Backend.cpp's fixture.
|
||||
class NodeStoreDatabaseTestBase : public ::testing::TestWithParam<std::string>
|
||||
{
|
||||
protected:
|
||||
void
|
||||
SetUp() override
|
||||
{
|
||||
nodeParams_.set("type", GetParam());
|
||||
nodeParams_.set("path", nodeDb_.path());
|
||||
|
||||
beast::xor_shift_engine rng(kSeedValue);
|
||||
batch_ = createPredictableBatch(kNumObjects, rng());
|
||||
}
|
||||
|
||||
std::unique_ptr<Database>
|
||||
makeDatabase()
|
||||
{
|
||||
return Manager::instance().makeDatabase(megabytes(4), scheduler_, 2, nodeParams_, journal_);
|
||||
}
|
||||
|
||||
DummyScheduler scheduler_;
|
||||
beast::TempDir const nodeDb_;
|
||||
beast::Journal const journal_{TestSink::instance()};
|
||||
Section nodeParams_;
|
||||
Batch batch_;
|
||||
};
|
||||
|
||||
class NodeStoreDatabaseTest : public NodeStoreDatabaseTestBase
|
||||
{
|
||||
};
|
||||
|
||||
class NodeStoreDatabasePersistenceTest : public NodeStoreDatabaseTestBase
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(NodeStoreDatabaseTest, store_and_fetch)
|
||||
{
|
||||
auto db = makeDatabase();
|
||||
|
||||
storeBatch(*db, batch_);
|
||||
|
||||
{
|
||||
SCOPED_TRACE("read in original order");
|
||||
auto const copy = fetchCopyOfBatch(*db, batch_);
|
||||
EXPECT_EQ(batch_, copy);
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TRACE("read in shuffled order");
|
||||
beast::xor_shift_engine rng(kSeedValue);
|
||||
std::shuffle(batch_.begin(), batch_.end(), rng);
|
||||
auto const copy = fetchCopyOfBatch(*db, batch_);
|
||||
EXPECT_EQ(batch_, copy);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(NodeStoreDatabasePersistenceTest, round_trip)
|
||||
{
|
||||
{
|
||||
auto db = makeDatabase();
|
||||
storeBatch(*db, batch_);
|
||||
}
|
||||
|
||||
// re-open without the ephemeral db
|
||||
auto db = makeDatabase();
|
||||
|
||||
auto copy = fetchCopyOfBatch(*db, batch_);
|
||||
std::ranges::sort(batch_, LessThan{});
|
||||
std::ranges::sort(copy, LessThan{});
|
||||
EXPECT_EQ(batch_, copy);
|
||||
}
|
||||
|
||||
// missing-key path at the Database layer. Mirrors Backend's fetch_missing —
|
||||
// fetching keys that were never stored must return nullptr (NotFound).
|
||||
TEST_P(NodeStoreDatabaseTest, fetch_missing)
|
||||
{
|
||||
auto db = makeDatabase();
|
||||
|
||||
// never store: every key must be absent
|
||||
fetchMissing(*db, batch_);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
NodeStoreBackends,
|
||||
NodeStoreDatabaseTest,
|
||||
::testing::ValuesIn(allBackends()),
|
||||
[](::testing::TestParamInfo<std::string> const& info) { return info.param; });
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
PersistentBackends,
|
||||
NodeStoreDatabasePersistenceTest,
|
||||
::testing::ValuesIn(persistentBackends()),
|
||||
[](::testing::TestParamInfo<std::string> const& info) { return info.param; });
|
||||
|
||||
TEST(NodeStoreDatabase, memory_earliest_seq)
|
||||
{
|
||||
DummyScheduler scheduler;
|
||||
beast::TempDir const nodeDb;
|
||||
Section nodeParams;
|
||||
nodeParams.set("type", "memory");
|
||||
nodeParams.set("path", nodeDb.path());
|
||||
|
||||
beast::Journal const journal(TestSink::instance());
|
||||
|
||||
// default earliest ledger sequence
|
||||
{
|
||||
auto db = Manager::instance().makeDatabase(megabytes(4), scheduler, 2, nodeParams, journal);
|
||||
EXPECT_EQ(db->earliestLedgerSeq(), kXrpLedgerEarliestSeq);
|
||||
}
|
||||
|
||||
// invalid earliest_seq value
|
||||
{
|
||||
nodeParams.set("earliest_seq", "0");
|
||||
try
|
||||
{
|
||||
auto db =
|
||||
Manager::instance().makeDatabase(megabytes(4), scheduler, 2, nodeParams, journal);
|
||||
FAIL() << "expected runtime_error for earliest_seq=0";
|
||||
}
|
||||
catch (std::runtime_error const& e)
|
||||
{
|
||||
EXPECT_STREQ(e.what(), "Invalid earliest_seq");
|
||||
}
|
||||
}
|
||||
|
||||
// valid earliest_seq value
|
||||
{
|
||||
nodeParams.set("earliest_seq", "1");
|
||||
auto db = Manager::instance().makeDatabase(megabytes(4), scheduler, 2, nodeParams, journal);
|
||||
EXPECT_EQ(db->earliestLedgerSeq(), 1u);
|
||||
}
|
||||
}
|
||||
|
||||
class DatabaseImportTest : public ::testing::TestWithParam<std::string>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(DatabaseImportTest, same_backend)
|
||||
{
|
||||
auto const type = GetParam();
|
||||
|
||||
DummyScheduler scheduler;
|
||||
beast::Journal const journal(TestSink::instance());
|
||||
|
||||
beast::TempDir const srcDir;
|
||||
Section srcParams;
|
||||
srcParams.set("type", type);
|
||||
srcParams.set("path", srcDir.path());
|
||||
|
||||
auto batch = createPredictableBatch(kNumObjects, kSeedValue);
|
||||
|
||||
// write to source db
|
||||
{
|
||||
auto src = Manager::instance().makeDatabase(megabytes(4), scheduler, 2, srcParams, journal);
|
||||
storeBatch(*src, batch);
|
||||
}
|
||||
|
||||
Batch copy;
|
||||
{
|
||||
// re-open source and import into a fresh destination
|
||||
auto src = Manager::instance().makeDatabase(megabytes(4), scheduler, 2, srcParams, journal);
|
||||
|
||||
beast::TempDir const destDir;
|
||||
Section destParams;
|
||||
destParams.set("type", type);
|
||||
destParams.set("path", destDir.path());
|
||||
|
||||
auto dest =
|
||||
Manager::instance().makeDatabase(megabytes(4), scheduler, 2, destParams, journal);
|
||||
|
||||
dest->importDatabase(*src);
|
||||
copy = fetchCopyOfBatch(*dest, batch);
|
||||
}
|
||||
|
||||
std::ranges::sort(batch, LessThan{});
|
||||
std::ranges::sort(copy, LessThan{});
|
||||
EXPECT_EQ(batch, copy);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ImportBackends,
|
||||
DatabaseImportTest,
|
||||
::testing::ValuesIn(importBackends()),
|
||||
[](::testing::TestParamInfo<std::string> const& info) { return info.param; });
|
||||
|
||||
} // namespace xrpl::node_store
|
||||
297
src/tests/libxrpl/nodestore/NuDBFactory.cpp
Normal file
297
src/tests/libxrpl/nodestore/NuDBFactory.cpp
Normal file
@@ -0,0 +1,297 @@
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
#include <xrpl/beast/utility/temp_dir.h>
|
||||
#include <xrpl/config/BasicConfig.h>
|
||||
#include <xrpl/nodestore/DummyScheduler.h>
|
||||
#include <xrpl/nodestore/Manager.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <helpers/CaptureSink.h>
|
||||
#include <helpers/TestSink.h>
|
||||
#include <nodestore/TestBase.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl::node_store {
|
||||
|
||||
namespace {
|
||||
|
||||
Section
|
||||
makeSection(std::string const& path, std::string const& blockSize = "")
|
||||
{
|
||||
Section params;
|
||||
params.set("type", "nudb");
|
||||
params.set("path", path);
|
||||
if (!blockSize.empty())
|
||||
params.set("nudb_block_size", blockSize);
|
||||
return params;
|
||||
}
|
||||
|
||||
void
|
||||
runRoundTrip(Section const& params, std::size_t expectedBlocksize)
|
||||
{
|
||||
DummyScheduler scheduler;
|
||||
beast::Journal const journal(TestSink::instance());
|
||||
auto backend = Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
|
||||
ASSERT_TRUE(backend);
|
||||
ASSERT_EQ(backend->getBlockSize(), expectedBlocksize);
|
||||
backend->open();
|
||||
ASSERT_TRUE(backend->isOpen());
|
||||
|
||||
auto const batch = createPredictableBatch(10, 12345);
|
||||
storeBatch(*backend, batch);
|
||||
|
||||
auto const copy = fetchCopyOfBatch(*backend, batch);
|
||||
|
||||
backend->close();
|
||||
EXPECT_EQ(batch, copy);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(NuDBFactory, default_block_size)
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto const params = makeSection(tempDir.path());
|
||||
ASSERT_NO_FATAL_FAILURE(runRoundTrip(params, 4096));
|
||||
}
|
||||
|
||||
TEST(NuDBFactory, valid_block_sizes)
|
||||
{
|
||||
auto const kValidSizes = std::to_array<std::size_t>({4096, 8192, 16384, 32768});
|
||||
for (auto const size : kValidSizes)
|
||||
{
|
||||
SCOPED_TRACE("size=" + std::to_string(size));
|
||||
beast::TempDir const tempDir;
|
||||
auto const params = makeSection(tempDir.path(), std::to_string(size));
|
||||
ASSERT_NO_FATAL_FAILURE(runRoundTrip(params, size));
|
||||
}
|
||||
|
||||
// empty value is ignored by config parser; default (4096) is used
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto const params = makeSection(tempDir.path(), "");
|
||||
ASSERT_NO_FATAL_FAILURE(runRoundTrip(params, 4096));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(NuDBFactory, invalid_block_sizes)
|
||||
{
|
||||
std::vector<std::string> const kInvalidSizes = {
|
||||
"2048", // too small
|
||||
"1024", // too small
|
||||
"65536", // too large
|
||||
"131072", // too large
|
||||
"5000", // not power of 2
|
||||
"6000", // not power of 2
|
||||
"10000", // not power of 2
|
||||
"0", // zero
|
||||
"-1", // negative
|
||||
"abc", // non-numeric
|
||||
"4k", // invalid format
|
||||
"4096.5"}; // decimal
|
||||
|
||||
for (auto const& size : kInvalidSizes)
|
||||
{
|
||||
SCOPED_TRACE("size='" + size + "'");
|
||||
beast::TempDir const tempDir;
|
||||
auto const params = makeSection(tempDir.path(), size);
|
||||
EXPECT_THROW(runRoundTrip(params, 4096), std::exception);
|
||||
}
|
||||
|
||||
// whitespace handling — lexical_cast may or may not strip; treat as invalid
|
||||
std::vector<std::string> const kWhitespaceSizes = {"4096 ", " 4096"};
|
||||
for (auto const& size : kWhitespaceSizes)
|
||||
{
|
||||
SCOPED_TRACE("size='" + size + "'");
|
||||
beast::TempDir const tempDir;
|
||||
auto const params = makeSection(tempDir.path(), size);
|
||||
EXPECT_THROW(runRoundTrip(params, 4096), std::exception);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(NuDBFactory, log_messages)
|
||||
{
|
||||
// valid custom block size emits info log
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto const params = makeSection(tempDir.path(), "8192");
|
||||
test::CaptureSink sink(beast::Severity::Info);
|
||||
beast::Journal const journal(sink);
|
||||
|
||||
DummyScheduler scheduler;
|
||||
[[maybe_unused]] auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
|
||||
EXPECT_TRUE(sink.messages().contains("Using custom NuDB block size: 8192"));
|
||||
}
|
||||
|
||||
// invalid block size throws with informative message
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto const params = makeSection(tempDir.path(), "5000");
|
||||
test::CaptureSink sink(beast::Severity::Warning);
|
||||
beast::Journal const journal(sink);
|
||||
DummyScheduler scheduler;
|
||||
try
|
||||
{
|
||||
auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
FAIL() << "expected exception for invalid block size 5000";
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
std::string const what{e.what()};
|
||||
EXPECT_TRUE(what.contains("Invalid nudb_block_size: 5000"));
|
||||
EXPECT_TRUE(what.contains("Must be power of 2 between 4096 and 32768"));
|
||||
}
|
||||
}
|
||||
|
||||
// non-numeric value throws
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto const params = makeSection(tempDir.path(), "invalid");
|
||||
test::CaptureSink sink(beast::Severity::Warning);
|
||||
beast::Journal const journal(sink);
|
||||
DummyScheduler scheduler;
|
||||
try
|
||||
{
|
||||
auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
FAIL() << "expected exception for non-numeric block size";
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
std::string const what{e.what()};
|
||||
EXPECT_TRUE(what.contains("Invalid nudb_block_size value: invalid"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(NuDBFactory, power_of_two_validation)
|
||||
{
|
||||
std::vector<std::pair<std::string, bool>> const kCASES = {
|
||||
{"4095", false}, // just below minimum
|
||||
{"4096", true}, // minimum valid
|
||||
{"4097", false}, // not power of 2
|
||||
{"8192", true}, // valid power of 2
|
||||
{"8193", false}, // not power of 2
|
||||
{"16384", true}, // valid power of 2
|
||||
{"32768", true}, // maximum valid
|
||||
{"32769", false}, // just above maximum
|
||||
{"65536", false}}; // power of 2 but too large
|
||||
|
||||
for (auto const& [size, shouldWork] : kCASES)
|
||||
{
|
||||
SCOPED_TRACE("size=" + size + " shouldWork=" + (shouldWork ? "true" : "false"));
|
||||
beast::TempDir const tempDir;
|
||||
auto const params = makeSection(tempDir.path(), size);
|
||||
test::CaptureSink sink(beast::Severity::Warning);
|
||||
beast::Journal const journal(sink);
|
||||
DummyScheduler scheduler;
|
||||
try
|
||||
{
|
||||
auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
EXPECT_TRUE(shouldWork);
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
// A throw is only expected for sizes that should NOT work; if a
|
||||
// valid size throws, fail here instead of silently matching the
|
||||
// message below (which would mask the regression).
|
||||
EXPECT_FALSE(shouldWork);
|
||||
std::string const what{e.what()};
|
||||
EXPECT_TRUE(what.contains("Invalid nudb_block_size"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(NuDBFactory, both_constructor_variants)
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto const params = makeSection(tempDir.path(), "16384");
|
||||
DummyScheduler scheduler;
|
||||
beast::Journal const journal(TestSink::instance());
|
||||
|
||||
auto backend1 = Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
EXPECT_NE(backend1, nullptr);
|
||||
ASSERT_NO_FATAL_FAILURE(runRoundTrip(params, 16384));
|
||||
|
||||
// Test second constructor (with nudb::context)
|
||||
// Note: This would require access to nudb::context, which might not be
|
||||
// easily testable without more complex setup. For now, we test that
|
||||
// the factory can create backends with the first constructor.
|
||||
}
|
||||
|
||||
TEST(NuDBFactory, configuration_parsing)
|
||||
{
|
||||
// basic valid format emits success log
|
||||
{
|
||||
beast::TempDir const tempDir;
|
||||
auto const params = makeSection(tempDir.path(), "8192");
|
||||
test::CaptureSink sink(beast::Severity::Info);
|
||||
beast::Journal const journal(sink);
|
||||
DummyScheduler scheduler;
|
||||
[[maybe_unused]] auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
EXPECT_TRUE(sink.messages().contains("Using custom NuDB block size"));
|
||||
}
|
||||
|
||||
// Test whitespace handling separately since lexical_cast behavior may vary
|
||||
std::vector<std::string> const kWhitespaceFormats = {" 8192", "8192 "};
|
||||
for (auto const& format : kWhitespaceFormats)
|
||||
{
|
||||
SCOPED_TRACE("format='" + format + "'");
|
||||
beast::TempDir const tempDir;
|
||||
auto const params = makeSection(tempDir.path(), format);
|
||||
test::CaptureSink sink(beast::Severity::Debug);
|
||||
beast::Journal const journal(sink);
|
||||
DummyScheduler scheduler;
|
||||
EXPECT_ANY_THROW(Manager::instance().makeBackend(params, megabytes(4), scheduler, journal));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(NuDBFactory, data_persistence)
|
||||
{
|
||||
std::vector<std::string> const kBlockSizes = {"4096", "8192", "16384", "32768"};
|
||||
for (auto const& size : kBlockSizes)
|
||||
{
|
||||
SCOPED_TRACE("size=" + size);
|
||||
beast::TempDir const tempDir;
|
||||
auto const params = makeSection(tempDir.path(), size);
|
||||
DummyScheduler scheduler;
|
||||
beast::Journal const journal(TestSink::instance());
|
||||
|
||||
// Create test data
|
||||
auto const batch = createPredictableBatch(50, 54321);
|
||||
|
||||
// Store data
|
||||
{
|
||||
auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
backend->open();
|
||||
storeBatch(*backend, batch);
|
||||
backend->close();
|
||||
}
|
||||
|
||||
// Retrieve data in new backend instance
|
||||
{
|
||||
auto backend =
|
||||
Manager::instance().makeBackend(params, megabytes(4), scheduler, journal);
|
||||
backend->open();
|
||||
auto const copy = fetchCopyOfBatch(*backend, batch);
|
||||
EXPECT_EQ(batch, copy);
|
||||
backend->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xrpl::node_store
|
||||
169
src/tests/libxrpl/nodestore/TestBase.h
Normal file
169
src/tests/libxrpl/nodestore/TestBase.h
Normal file
@@ -0,0 +1,169 @@
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/random.h>
|
||||
#include <xrpl/beast/utility/rngfill.h>
|
||||
#include <xrpl/beast/xor_shift_engine.h>
|
||||
#include <xrpl/nodestore/Backend.h>
|
||||
#include <xrpl/nodestore/Database.h>
|
||||
#include <xrpl/nodestore/NodeObject.h>
|
||||
#include <xrpl/nodestore/Types.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace xrpl::node_store {
|
||||
|
||||
constexpr std::size_t kMinPayloadBytes = 1;
|
||||
constexpr std::size_t kMaxPayloadBytes = 2000;
|
||||
constexpr int kNumObjectsToTest = 2000;
|
||||
constexpr int kNumObjects = 2000;
|
||||
constexpr std::uint64_t kSeedValue = 50;
|
||||
|
||||
struct LessThan
|
||||
{
|
||||
bool
|
||||
operator()(std::shared_ptr<NodeObject> const& lhs, std::shared_ptr<NodeObject> const& rhs)
|
||||
const noexcept
|
||||
{
|
||||
return lhs->getHash() < rhs->getHash();
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
isSame(std::shared_ptr<NodeObject> const& lhs, std::shared_ptr<NodeObject> const& rhs)
|
||||
{
|
||||
return (lhs->getType() == rhs->getType()) && (lhs->getHash() == rhs->getHash()) &&
|
||||
(lhs->getData() == rhs->getData());
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Batch
|
||||
createPredictableBatch(std::size_t numObjects, std::uint64_t seed)
|
||||
{
|
||||
Batch batch;
|
||||
batch.reserve(numObjects);
|
||||
|
||||
beast::xor_shift_engine rng(seed);
|
||||
|
||||
for (auto i = 0uz; i < numObjects; ++i)
|
||||
{
|
||||
NodeObjectType const type = [&] {
|
||||
switch (randInt(rng, 3))
|
||||
{
|
||||
case 0:
|
||||
return NodeObjectType::Ledger;
|
||||
case 1:
|
||||
return NodeObjectType::AccountNode;
|
||||
case 2:
|
||||
return NodeObjectType::TransactionNode;
|
||||
case 3:
|
||||
default:
|
||||
return NodeObjectType::Unknown;
|
||||
}
|
||||
}();
|
||||
|
||||
uint256 hash;
|
||||
beast::rngfill(hash.begin(), hash.size(), rng);
|
||||
|
||||
Blob blob(randInt(rng, kMinPayloadBytes, kMaxPayloadBytes));
|
||||
beast::rngfill(blob.data(), blob.size(), rng);
|
||||
|
||||
batch.emplace_back(NodeObject::createObject(type, std::move(blob), hash));
|
||||
}
|
||||
|
||||
return batch;
|
||||
}
|
||||
|
||||
inline void
|
||||
storeBatch(Backend& backend, Batch const& batch)
|
||||
{
|
||||
for (auto const& obj : batch)
|
||||
backend.store(obj);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Batch
|
||||
fetchCopyOfBatch(Backend& backend, Batch const& batch)
|
||||
{
|
||||
Batch copy;
|
||||
copy.reserve(batch.size());
|
||||
|
||||
for (auto i = 0uz; i < batch.size(); ++i)
|
||||
{
|
||||
SCOPED_TRACE("fetchCopyOfBatch index=" + std::to_string(i));
|
||||
std::shared_ptr<NodeObject> object;
|
||||
Status const status = backend.fetch(batch[i]->getHash(), &object);
|
||||
EXPECT_EQ(status, Status::Ok);
|
||||
if (status == Status::Ok)
|
||||
{
|
||||
EXPECT_NE(object, nullptr);
|
||||
copy.emplace_back(object);
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
inline void
|
||||
fetchMissing(Backend& backend, Batch const& batch)
|
||||
{
|
||||
for (auto i = 0uz; i < batch.size(); ++i)
|
||||
{
|
||||
SCOPED_TRACE("fetchMissing index=" + std::to_string(i));
|
||||
std::shared_ptr<NodeObject> object;
|
||||
Status const status = backend.fetch(batch[i]->getHash(), &object);
|
||||
EXPECT_EQ(status, Status::NotFound);
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
storeBatch(Database& db, Batch const& batch)
|
||||
{
|
||||
for (auto const& obj : batch)
|
||||
{
|
||||
Blob data(obj->getData());
|
||||
db.store(obj->getType(), std::move(data), obj->getHash(), db.earliestLedgerSeq());
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Batch
|
||||
fetchCopyOfBatch(Database& db, Batch const& batch)
|
||||
{
|
||||
Batch copy;
|
||||
copy.reserve(batch.size());
|
||||
|
||||
for (auto const& obj : batch)
|
||||
{
|
||||
std::shared_ptr<NodeObject> const result = db.fetchNodeObject(obj->getHash(), 0);
|
||||
if (result != nullptr)
|
||||
copy.emplace_back(result);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
inline void
|
||||
fetchMissing(Database& db, Batch const& batch)
|
||||
{
|
||||
for (auto i = 0uz; i < batch.size(); ++i)
|
||||
{
|
||||
SCOPED_TRACE("fetchMissing(Database) index=" + std::to_string(i));
|
||||
EXPECT_EQ(db.fetchNodeObject(batch[i]->getHash(), 0), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xrpl::node_store
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
operator==(node_store::Batch const& lhs, node_store::Batch const& rhs)
|
||||
{
|
||||
return std::ranges::equal(lhs, rhs, node_store::isSame);
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
46
src/tests/libxrpl/nodestore/varint.cpp
Normal file
46
src/tests/libxrpl/nodestore/varint.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <xrpl/nodestore/detail/varint.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace xrpl::node_store;
|
||||
|
||||
TEST(varint, encode_decode)
|
||||
{
|
||||
std::vector<std::size_t> const kVALUES = {
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
126,
|
||||
127,
|
||||
128,
|
||||
253,
|
||||
254,
|
||||
255,
|
||||
16127,
|
||||
16128,
|
||||
16129,
|
||||
0xff,
|
||||
0xffff,
|
||||
0xffffffff,
|
||||
0xffffffffffffUL,
|
||||
0xffffffffffffffffUL};
|
||||
|
||||
for (auto const v : kVALUES)
|
||||
{
|
||||
SCOPED_TRACE("value=" + std::to_string(v));
|
||||
std::array<std::uint8_t, varint_traits<std::size_t>::kMax> vi{};
|
||||
auto const n0 = writeVarint(vi.data(), v);
|
||||
EXPECT_GT(n0, 0u) << "write error";
|
||||
EXPECT_EQ(n0, sizeVarint(v)) << "size error";
|
||||
std::size_t v1 = 0;
|
||||
auto const n1 = readVarint(vi.data(), n0, v1);
|
||||
EXPECT_EQ(n1, n0) << "read error";
|
||||
EXPECT_EQ(v1, v) << "wrong value";
|
||||
}
|
||||
}
|
||||
@@ -24,13 +24,13 @@ namespace xrpl::tests {
|
||||
class TestNodeFamily : public Family
|
||||
{
|
||||
private:
|
||||
std::unique_ptr<NodeStore::Database> db_;
|
||||
std::unique_ptr<node_store::Database> db_;
|
||||
|
||||
std::shared_ptr<FullBelowCache> fbCache_;
|
||||
std::shared_ptr<TreeNodeCache> tnCache_;
|
||||
|
||||
TestStopwatch clock_;
|
||||
NodeStore::DummyScheduler scheduler_;
|
||||
node_store::DummyScheduler scheduler_;
|
||||
|
||||
beast::Journal const j_;
|
||||
|
||||
@@ -49,17 +49,17 @@ public:
|
||||
Section testSection;
|
||||
testSection.set(Keys::kType, "memory");
|
||||
testSection.set(Keys::kPath, "SHAMap_test");
|
||||
db_ = NodeStore::Manager::instance().makeDatabase(
|
||||
db_ = node_store::Manager::instance().makeDatabase(
|
||||
megabytes(4), scheduler_, 1, testSection, j);
|
||||
}
|
||||
|
||||
NodeStore::Database&
|
||||
node_store::Database&
|
||||
db() override
|
||||
{
|
||||
return *db_;
|
||||
}
|
||||
|
||||
[[nodiscard]] NodeStore::Database const&
|
||||
[[nodiscard]] node_store::Database const&
|
||||
db() const override
|
||||
{
|
||||
return *db_;
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace xrpl {
|
||||
class AccountStateSF : public SHAMapSyncFilter
|
||||
{
|
||||
public:
|
||||
AccountStateSF(NodeStore::Database& db, AbstractFetchPackContainer& fp) : db_(db), fp_(fp)
|
||||
AccountStateSF(node_store::Database& db, AbstractFetchPackContainer& fp) : db_(db), fp_(fp)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
getNode(SHAMapHash const& nodeHash) const override;
|
||||
|
||||
private:
|
||||
NodeStore::Database& db_;
|
||||
node_store::Database& db_;
|
||||
AbstractFetchPackContainer& fp_;
|
||||
};
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ private:
|
||||
addPeers();
|
||||
|
||||
void
|
||||
tryDB(NodeStore::Database& srcDB);
|
||||
tryDB(node_store::Database& srcDB);
|
||||
|
||||
void
|
||||
done();
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace xrpl {
|
||||
class TransactionStateSF : public SHAMapSyncFilter
|
||||
{
|
||||
public:
|
||||
TransactionStateSF(NodeStore::Database& db, AbstractFetchPackContainer& fp) : db_(db), fp_(fp)
|
||||
TransactionStateSF(node_store::Database& db, AbstractFetchPackContainer& fp) : db_(db), fp_(fp)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
getNode(SHAMapHash const& nodeHash) const override;
|
||||
|
||||
private:
|
||||
NodeStore::Database& db_;
|
||||
node_store::Database& db_;
|
||||
AbstractFetchPackContainer& fp_;
|
||||
};
|
||||
|
||||
|
||||
@@ -224,7 +224,7 @@ InboundLedger::neededStateHashes(int max, SHAMapSyncFilter const* filter) const
|
||||
// See how much of the ledger data is stored locally
|
||||
// Data found in a fetch pack will be stored
|
||||
void
|
||||
InboundLedger::tryDB(NodeStore::Database& srcDB)
|
||||
InboundLedger::tryDB(node_store::Database& srcDB)
|
||||
{
|
||||
if (!haveHeader_)
|
||||
{
|
||||
|
||||
@@ -653,7 +653,7 @@ LedgerMaster::tryFill(std::shared_ptr<Ledger const> ledger)
|
||||
std::uint32_t minHas = seq;
|
||||
std::uint32_t maxHas = seq;
|
||||
|
||||
NodeStore::Database& nodeStore{app_.getNodeStore()};
|
||||
node_store::Database& nodeStore{app_.getNodeStore()};
|
||||
while (!app_.getJobQueue().isStopping() && seq > 0)
|
||||
{
|
||||
{
|
||||
|
||||
@@ -232,7 +232,7 @@ public:
|
||||
|
||||
std::unique_ptr<Resource::Manager> resourceManager_;
|
||||
|
||||
std::unique_ptr<NodeStore::Database> nodeStore_;
|
||||
std::unique_ptr<node_store::Database> nodeStore_;
|
||||
NodeFamily nodeFamily_;
|
||||
std::unique_ptr<OrderBookDB> orderBookDB_;
|
||||
std::unique_ptr<PathRequestManager> pathRequestManager_;
|
||||
@@ -655,7 +655,7 @@ public:
|
||||
return tempNodeCache_;
|
||||
}
|
||||
|
||||
NodeStore::Database&
|
||||
node_store::Database&
|
||||
getNodeStore() override
|
||||
{
|
||||
return *nodeStore_;
|
||||
@@ -860,9 +860,9 @@ public:
|
||||
if (config_->doImport)
|
||||
{
|
||||
auto j = logs_->journal("NodeObject");
|
||||
NodeStore::DummyScheduler dummyScheduler;
|
||||
std::unique_ptr<NodeStore::Database> source =
|
||||
NodeStore::Manager::instance().makeDatabase(
|
||||
node_store::DummyScheduler dummyScheduler;
|
||||
std::unique_ptr<node_store::Database> source =
|
||||
node_store::Manager::instance().makeDatabase(
|
||||
megabytes(config_->getValueFor(SizedItem::BurstSize, std::nullopt)),
|
||||
dummyScheduler,
|
||||
0,
|
||||
|
||||
@@ -12,7 +12,7 @@ NodeStoreScheduler::NodeStoreScheduler(JobQueue& jobQueue) : jobQueue_(jobQueue)
|
||||
}
|
||||
|
||||
void
|
||||
NodeStoreScheduler::scheduleTask(NodeStore::Task& task)
|
||||
NodeStoreScheduler::scheduleTask(node_store::Task& task)
|
||||
{
|
||||
if (jobQueue_.isStopped())
|
||||
return;
|
||||
@@ -26,19 +26,19 @@ NodeStoreScheduler::scheduleTask(NodeStore::Task& task)
|
||||
}
|
||||
|
||||
void
|
||||
NodeStoreScheduler::onFetch(NodeStore::FetchReport const& report)
|
||||
NodeStoreScheduler::onFetch(node_store::FetchReport const& report)
|
||||
{
|
||||
if (jobQueue_.isStopped())
|
||||
return;
|
||||
|
||||
jobQueue_.addLoadEvents(
|
||||
report.fetchType == NodeStore::FetchType::Async ? JtNsAsyncRead : JtNsSyncRead,
|
||||
report.fetchType == node_store::FetchType::Async ? JtNsAsyncRead : JtNsSyncRead,
|
||||
1,
|
||||
report.elapsed);
|
||||
}
|
||||
|
||||
void
|
||||
NodeStoreScheduler::onBatchWrite(NodeStore::BatchWriteReport const& report)
|
||||
NodeStoreScheduler::onBatchWrite(node_store::BatchWriteReport const& report)
|
||||
{
|
||||
if (jobQueue_.isStopped())
|
||||
return;
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
namespace xrpl {
|
||||
|
||||
/**
|
||||
* A NodeStore::Scheduler which uses the JobQueue.
|
||||
* A node_store::Scheduler which uses the JobQueue.
|
||||
*/
|
||||
class NodeStoreScheduler : public NodeStore::Scheduler
|
||||
class NodeStoreScheduler : public node_store::Scheduler
|
||||
{
|
||||
public:
|
||||
explicit NodeStoreScheduler(JobQueue& jobQueue);
|
||||
|
||||
void
|
||||
scheduleTask(NodeStore::Task& task) override;
|
||||
scheduleTask(node_store::Task& task) override;
|
||||
void
|
||||
onFetch(NodeStore::FetchReport const& report) override;
|
||||
onFetch(node_store::FetchReport const& report) override;
|
||||
void
|
||||
onBatchWrite(NodeStore::BatchWriteReport const& report) override;
|
||||
onBatchWrite(node_store::BatchWriteReport const& report) override;
|
||||
|
||||
private:
|
||||
JobQueue& jobQueue_;
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
[[nodiscard]] virtual std::uint32_t
|
||||
clampFetchDepth(std::uint32_t fetchDepth) const = 0;
|
||||
|
||||
virtual std::unique_ptr<NodeStore::Database>
|
||||
virtual std::unique_ptr<node_store::Database>
|
||||
makeNodeStore(int readThreads) = 0;
|
||||
|
||||
/**
|
||||
@@ -101,5 +101,5 @@ public:
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
std::unique_ptr<SHAMapStore>
|
||||
makeSHAMapStore(Application& app, NodeStore::Scheduler& scheduler, beast::Journal journal);
|
||||
makeSHAMapStore(Application& app, node_store::Scheduler& scheduler, beast::Journal journal);
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -94,7 +94,7 @@ SHAMapStoreImp::SavedStateDB::setLastRotated(LedgerIndex seq)
|
||||
|
||||
SHAMapStoreImp::SHAMapStoreImp(
|
||||
Application& app,
|
||||
NodeStore::Scheduler& scheduler,
|
||||
node_store::Scheduler& scheduler,
|
||||
beast::Journal journal)
|
||||
: app_(app)
|
||||
, scheduler_(scheduler)
|
||||
@@ -165,7 +165,7 @@ SHAMapStoreImp::SHAMapStoreImp(
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<NodeStore::Database>
|
||||
std::unique_ptr<node_store::Database>
|
||||
SHAMapStoreImp::makeNodeStore(int readThreads)
|
||||
{
|
||||
auto nscfg = app_.config().section(Sections::kNodeDatabase);
|
||||
@@ -185,7 +185,7 @@ SHAMapStoreImp::makeNodeStore(int readThreads)
|
||||
std::to_string(app_.config().getValueFor(SizedItem::TreeCacheAge, std::nullopt)));
|
||||
}
|
||||
|
||||
std::unique_ptr<NodeStore::Database> db;
|
||||
std::unique_ptr<node_store::Database> db;
|
||||
|
||||
if (deleteInterval_ != 0u)
|
||||
{
|
||||
@@ -201,7 +201,7 @@ SHAMapStoreImp::makeNodeStore(int readThreads)
|
||||
|
||||
// Create NodeStore with two backends to allow online deletion of
|
||||
// data
|
||||
auto dbr = std::make_unique<NodeStore::DatabaseRotatingImp>(
|
||||
auto dbr = std::make_unique<node_store::DatabaseRotatingImp>(
|
||||
scheduler_,
|
||||
readThreads,
|
||||
std::move(writableBackend),
|
||||
@@ -210,11 +210,11 @@ SHAMapStoreImp::makeNodeStore(int readThreads)
|
||||
app_.getJournal(kNodeStoreName));
|
||||
fdRequired_ += dbr->fdRequired();
|
||||
dbRotating_ = dbr.get();
|
||||
db.reset(dynamic_cast<NodeStore::Database*>(dbr.release()));
|
||||
db.reset(dynamic_cast<node_store::Database*>(dbr.release()));
|
||||
}
|
||||
else
|
||||
{
|
||||
db = NodeStore::Manager::instance().makeDatabase(
|
||||
db = node_store::Manager::instance().makeDatabase(
|
||||
megabytes(app_.config().getValueFor(SizedItem::BurstSize, std::nullopt)),
|
||||
scheduler_,
|
||||
readThreads,
|
||||
@@ -257,7 +257,7 @@ SHAMapStoreImp::copyNode(std::uint64_t& nodeCount, SHAMapTreeNode const& node)
|
||||
{
|
||||
// Copy a single record from node to dbRotating_
|
||||
auto obj = dbRotating_->fetchNodeObject(
|
||||
node.getHash().asUInt256(), 0, NodeStore::FetchType::Synchronous, true);
|
||||
node.getHash().asUInt256(), 0, node_store::FetchType::Synchronous, true);
|
||||
if (!obj)
|
||||
{
|
||||
XRPL_ASSERT(node.cowid() == 0, "SHAMapStoreImp::copyNode : rescued node must be clean");
|
||||
@@ -374,7 +374,7 @@ SHAMapStoreImp::run()
|
||||
// exception) also clear the flag.
|
||||
struct RotationExposureGuard
|
||||
{
|
||||
NodeStore::DatabaseRotating& db;
|
||||
node_store::DatabaseRotating& db;
|
||||
~RotationExposureGuard()
|
||||
{
|
||||
db.setRotationInFlight(false);
|
||||
@@ -516,7 +516,7 @@ SHAMapStoreImp::dbPaths()
|
||||
boost::filesystem::remove_all(p);
|
||||
}
|
||||
|
||||
std::unique_ptr<NodeStore::Backend>
|
||||
std::unique_ptr<node_store::Backend>
|
||||
SHAMapStoreImp::makeBackendRotating(std::string path)
|
||||
{
|
||||
Section section{app_.config().section(Sections::kNodeDatabase)};
|
||||
@@ -535,7 +535,7 @@ SHAMapStoreImp::makeBackendRotating(std::string path)
|
||||
}
|
||||
section.set(Keys::kPath, newPath.string());
|
||||
|
||||
auto backend{NodeStore::Manager::instance().makeBackend(
|
||||
auto backend{node_store::Manager::instance().makeBackend(
|
||||
section,
|
||||
megabytes(app_.config().getValueFor(SizedItem::BurstSize, std::nullopt)),
|
||||
scheduler_,
|
||||
@@ -702,7 +702,7 @@ SHAMapStoreImp::minimumOnline() const
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
std::unique_ptr<SHAMapStore>
|
||||
makeSHAMapStore(Application& app, NodeStore::Scheduler& scheduler, beast::Journal journal)
|
||||
makeSHAMapStore(Application& app, node_store::Scheduler& scheduler, beast::Journal journal)
|
||||
{
|
||||
return std::make_unique<SHAMapStoreImp>(app, scheduler, journal);
|
||||
}
|
||||
|
||||
@@ -81,9 +81,9 @@ private:
|
||||
// minimum ledger to maintain online.
|
||||
std::atomic<LedgerIndex> minimumOnline_;
|
||||
|
||||
NodeStore::Scheduler& scheduler_;
|
||||
node_store::Scheduler& scheduler_;
|
||||
beast::Journal const journal_;
|
||||
NodeStore::DatabaseRotating* dbRotating_ = nullptr;
|
||||
node_store::DatabaseRotating* dbRotating_ = nullptr;
|
||||
SavedStateDB stateDb_;
|
||||
std::thread thread_;
|
||||
bool stop_ = false;
|
||||
@@ -119,7 +119,7 @@ private:
|
||||
static constexpr auto kNodeStoreName = "NodeStore";
|
||||
|
||||
public:
|
||||
SHAMapStoreImp(Application& app, NodeStore::Scheduler& scheduler, beast::Journal journal);
|
||||
SHAMapStoreImp(Application& app, node_store::Scheduler& scheduler, beast::Journal journal);
|
||||
|
||||
std::uint32_t
|
||||
clampFetchDepth(std::uint32_t fetchDepth) const override
|
||||
@@ -127,7 +127,7 @@ public:
|
||||
return (deleteInterval_ != 0u) ? std::min(fetchDepth, deleteInterval_) : fetchDepth;
|
||||
}
|
||||
|
||||
std::unique_ptr<NodeStore::Database>
|
||||
std::unique_ptr<node_store::Database>
|
||||
makeNodeStore(int readThreads) override;
|
||||
|
||||
LedgerIndex
|
||||
@@ -180,7 +180,7 @@ private:
|
||||
void
|
||||
dbPaths();
|
||||
|
||||
std::unique_ptr<NodeStore::Backend>
|
||||
std::unique_ptr<node_store::Backend>
|
||||
makeBackendRotating(std::string path = std::string());
|
||||
|
||||
template <class CacheInstance>
|
||||
@@ -191,7 +191,7 @@ private:
|
||||
|
||||
for (auto const& key : cache.getKeys())
|
||||
{
|
||||
dbRotating_->fetchNodeObject(key, 0, NodeStore::FetchType::Synchronous, true);
|
||||
dbRotating_->fetchNodeObject(key, 0, node_store::FetchType::Synchronous, true);
|
||||
if (!(++check % checkHealthInterval_) && healthWait() == HealthResult::Stopping)
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -33,13 +33,13 @@ public:
|
||||
|
||||
NodeFamily(Application& app, CollectorManager& cm);
|
||||
|
||||
NodeStore::Database&
|
||||
node_store::Database&
|
||||
db() override
|
||||
{
|
||||
return db_;
|
||||
}
|
||||
|
||||
[[nodiscard]] NodeStore::Database const&
|
||||
[[nodiscard]] node_store::Database const&
|
||||
db() const override
|
||||
{
|
||||
return db_;
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
|
||||
private:
|
||||
Application& app_;
|
||||
NodeStore::Database& db_;
|
||||
node_store::Database& db_;
|
||||
beast::Journal const j_;
|
||||
|
||||
std::shared_ptr<FullBelowCache> fbCache_;
|
||||
|
||||
Reference in New Issue
Block a user