20 #include <ripple/nodestore/impl/DatabaseShardImp.h>
21 #include <ripple/app/ledger/InboundLedgers.h>
22 #include <ripple/app/ledger/LedgerMaster.h>
23 #include <ripple/app/misc/NetworkOPs.h>
24 #include <ripple/basics/ByteUtilities.h>
25 #include <ripple/basics/chrono.h>
26 #include <ripple/basics/random.h>
27 #include <ripple/core/ConfigSections.h>
28 #include <ripple/nodestore/DummyScheduler.h>
29 #include <ripple/overlay/Overlay.h>
30 #include <ripple/overlay/predicates.h>
31 #include <ripple/protocol/HashPrefix.h>
33 #include <boost/algorithm/string/predicate.hpp>
56 , avgShardFileSz_(ledgersPerShard_ *
kilobytes(192))
72 JLOG(
j_.
error()) <<
"already initialized";
78 JLOG(
j_.
error()) <<
"invalid configuration file settings";
84 using namespace boost::filesystem;
87 if (!is_directory(
dir_))
89 JLOG(
j_.
error()) <<
"'path' must be a directory";
94 create_directories(
dir_);
96 ctx_ = std::make_unique<nudb::context>();
100 for (
auto const& d : directory_iterator(
dir_))
102 if (!is_directory(d))
106 auto dirName = d.path().stem().string();
111 return ::isdigit(static_cast<unsigned char>(c));
121 "shard " << shardIndex <<
122 " comes before earliest shard index " <<
133 "shard " << shardIndex <<
134 " previously failed import, removing";
135 remove_all(shardDir);
139 auto shard {std::make_unique<Shard>(
146 if (!shard->isLegacy())
150 shard->removeOnDestroy();
152 "shard " << shardIndex <<
153 " removed, legacy shard";
157 if (shard->isFinal())
163 else if (shard->isBackendComplete())
165 auto const result {
shards_.emplace(
175 "more than one shard being acquired";
189 "exception " << e.
what() <<
" in function " << __func__;
201 boost::optional<std::uint32_t>
204 boost::optional<std::uint32_t> shardIndex;
213 return it->second.shard->prepare();
224 JLOG(
j_.
debug()) <<
"maximum storage size reached";
230 JLOG(
j_.
error()) <<
"insufficient storage space available";
240 JLOG(
j_.
debug()) <<
"no new shards to add";
248 auto shard {std::make_unique<Shard>(
app_, *
this, *shardIndex,
j_)};
252 auto const seq {shard->prepare()};
268 JLOG(j.error()) <<
"shard " << shardIndex <<
" " << msg;
275 return fail(
"cannot be stored at this time");
279 return fail(
"comes before earliest shard index " +
289 return fail(
"has an invalid index");
301 "shard " << shardIndex <<
302 " is already stored or queued for import";
308 return fail(
"maximum storage size reached");
310 return fail(
"insufficient storage space available");
322 if (
auto const it {
shards_.find(shardIndex)};
351 boost::filesystem::path
const& srcDir)
353 using namespace boost::filesystem;
356 if (!is_directory(srcDir) || is_empty(srcDir))
359 "invalid source directory " << srcDir.string();
366 "exception " << e.
what() <<
" in function " << __func__;
370 auto renameDir = [&](path
const& src, path
const& dst)
379 "exception " << e.
what() <<
" in function " << __func__;
391 if (
auto const it {
shards_.find(shardIndex)};
397 "shard " << shardIndex <<
" failed to import";
405 if (!renameDir(srcDir, dstDir))
409 auto shard {std::make_unique<Shard>(
app_, *
this, shardIndex,
j_)};
410 if (!shard->open(
scheduler_, *
ctx_) || !shard->isBackendComplete())
413 "shard " << shardIndex <<
" failed to import";
415 renameDir(dstDir, srcDir);
420 auto const it {
shards_.find(shardIndex)};
426 "shard " << shardIndex <<
" failed to import";
430 it->second.shard = std::move(shard);
445 if (
auto const it {
shards_.find(shardIndex)}; it !=
shards_.end())
446 shardInfo = it->second;
453 switch (shardInfo.
state)
458 if (shardInfo.
shard->containsLedger(seq))
466 auto nObj {
fetch(hash, seq)};
476 auto ledger {std::make_shared<Ledger>(
481 if (ledger->info().seq != seq)
483 return fail(
"encountered invalid ledger sequence " +
486 if (ledger->info().hash != hash)
488 return fail(
"encountered invalid ledger hash " +
493 if (!ledger->stateMap().fetchRoot(
494 SHAMapHash {ledger->info().accountHash},
nullptr))
496 return fail(
"is missing root STATE node on hash " +
500 if (ledger->info().txHash.isNonZero())
502 if (!ledger->txMap().fetchRoot(
505 return fail(
"is missing root TXN node on hash " +
515 if (ledger->info().hash.isZero())
518 "zero ledger hash for ledger sequence " << ledger->info().seq;
521 if (ledger->info().accountHash.isZero())
524 "zero account hash for ledger sequence " << ledger->info().seq;
527 if (ledger->stateMap().getHash().isNonZero() &&
528 !ledger->stateMap().isValid())
531 "invalid state map for ledger sequence " << ledger->info().seq;
534 if (ledger->info().txHash.isNonZero() && !ledger->txMap().isValid())
537 "invalid transaction map for ledger sequence " <<
551 "shard " << shardIndex <<
" is not being acquired";
555 if (
auto const it {
shards_.find(shardIndex)}; it !=
shards_.end())
556 shard = it->second.shard;
560 "shard " << shardIndex <<
" is not being acquired";
596 for (
auto const& e : shards)
598 if (
auto shard {e.lock()}; shard)
599 shard->finalize(
true);
618 e.second.shard->stop();
633 JLOG(
j_.
error()) <<
"invalid source database";
640 auto loadLedger = [&](
bool ascendSort =
true) ->
641 boost::optional<std::uint32_t>
647 " order by LedgerSeq " + (ascendSort ?
"asc" :
"desc") +
648 " limit 1",
app_,
false);
649 if (!ledger || seq == 0)
652 "no suitable ledgers were found in"
653 " the SQLite database to import";
660 auto seq {loadLedger()};
670 seq = loadLedger(
false);
679 if (latestIndex < earliestIndex)
682 "no suitable ledgers were found in"
683 " the SQLite database to import";
690 shardIndex <= latestIndex; ++shardIndex)
694 JLOG(
j_.
error()) <<
"maximum storage size reached";
700 JLOG(
j_.
error()) <<
"insufficient storage space available";
710 "shard " << shardIndex <<
" already exists";
722 if (ledgerHashes.size() != numLedgers)
728 if (!source.
fetch(ledgerHashes[n].first, n))
731 "SQLite ledger sequence " << n <<
732 " mismatches node store";
743 auto shard {std::make_unique<Shard>(
app_, *
this, shardIndex,
j_)};
755 "shard " << shardIndex <<
756 " failed to create temp marker file";
757 shard->removeOnDestroy();
765 boost::optional<uint256> lastLedgerHash;
767 while (
auto seq = shard->prepare())
770 if (!ledger || ledger->info().seq != seq)
783 if (!shard->store(ledger))
787 lastLedgerHash = ledger->info().hash;
789 recentStored = ledger;
792 using namespace boost::filesystem;
793 if (lastLedgerHash && shard->isBackendComplete())
800 s.
add256(*lastLedgerHash);
808 shard->getBackend()->store(nObj);
812 remove_all(markerFile);
815 "shard " << shardIndex <<
816 " was successfully imported";
818 auto const result {
shards_.emplace(
826 "exception " << e.
what() <<
827 " in function " << __func__;
828 shard->removeOnDestroy();
834 "shard " << shardIndex <<
" failed to import";
835 shard->removeOnDestroy();
854 shard = it->second.shard;
859 return shard->getBackend()->getWriteLoad();
878 "shard " << shardIndex <<
" is not being acquired";
882 if (
auto const it {
shards_.find(shardIndex)}; it !=
shards_.end())
883 shard = it->second.shard;
887 "shard " << shardIndex <<
" is not being acquired";
892 auto [backend, pCache, nCache] = shard->getBackendAll();
895 pCache->canonicalize_replace_cache(hash, nObj);
896 backend->store(nObj);
907 return doFetch(hash, seq, *cache.first, *cache.second,
false);
921 object = cache.first->fetch(hash);
922 if (
object || cache.second->touch_if_exists(hash))
933 auto const seq {srcLedger->info().seq};
943 "shard " << shardIndex <<
" is not being acquired";
947 if (
auto const it {
shards_.find(shardIndex)}; it !=
shards_.end())
948 shard = it->second.shard;
952 "shard " << shardIndex <<
" is not being acquired";
957 if (shard->containsLedger(seq))
960 "shard " << shardIndex <<
" ledger already stored";
965 auto [backend, pCache, nCache] = shard->getBackendAll();
989 if (
auto const it {
shards_.find(shardIndex)};
994 shard = it->second.shard;
1000 return shard->pCache()->getTargetSize() /
asyncDivider;
1012 shard = it->second.shard;
1017 return shard->pCache()->getHitRate();
1036 for (
auto const& e : shards)
1038 if (
auto shard {e.lock()}; shard)
1065 get_if_exists<std::uint32_t>(
1068 shardDBEarliestSeq);
1071 get_if_exists<std::uint32_t>(
1076 if (shardDBEarliestSeq != nodeDBEarliestSeq)
1079 "] define different 'earliest_seq' values");
1083 using namespace boost::filesystem;
1084 if (!get_if_exists<path>(section,
"path",
dir_))
1085 return fail(
"'path' missing");
1089 if (!get_if_exists<std::uint64_t>(section,
"max_size_gb", sz))
1090 return fail(
"'max_size_gb' missing");
1092 if ((sz << 30) < sz)
1093 return fail(
"'max_size_gb' overflow");
1097 return fail(
"'max_size_gb' must be at least 10");
1103 if (section.exists(
"ledgers_per_shard"))
1106 if (!config.standalone())
1107 return fail(
"'ledgers_per_shard' only honored in stand alone");
1111 return fail(
"'ledgers_per_shard' must be a multiple of 256");
1115 backendName_ = get<std::string>(section,
"type",
"nudb");
1117 return fail(
"'type' value unsupported");
1131 if (
auto const it {
shards_.find(shardIndex)};
1135 shard = it->second.shard;
1144 boost::optional<std::uint32_t>
1152 auto const maxShardIndex {[
this, validLedgerSeq]()
1162 if (
shards_.size() >= maxNumShards)
1165 if (maxShardIndex < 1024 ||
1166 static_cast<float>(
shards_.size()) / maxNumShards > 0.5f)
1174 shardIndex <= maxShardIndex;
1194 for (
int i = 0; i < 40; ++i)
1211 assert(shardInfo.
shard);
1213 assert(shardInfo.
shard->isBackendComplete());
1216 auto const shardIndex {shardInfo.
shard->index()};
1219 taskQueue_->addTask([
this, shardIndex, writeSQLite]()
1227 if (
auto const it {
shards_.find(shardIndex)}; it !=
shards_.end())
1228 shard = it->second.shard;
1232 "Unable to finalize shard " << shardIndex;
1237 if (!shard->finalize(writeSQLite))
1249 shard->removeOnDestroy();
1260 auto const it {
shards_.find(shardIndex)};
1273 protocol::TMPeerShardInfo message;
1275 message.set_nodepubkey(publicKey.data(), publicKey.size());
1278 std::make_shared<Message>(
1280 protocol::mtPEER_SHARD_INFO)));
1304 for (
auto const& e : shards)
1306 if (
auto shard {e.lock()}; shard)
1308 auto[sz, fd] = shard->fileInfo();
1322 JLOG(
j_.
warn()) <<
"maximum storage size reached";
1328 "maximum shard store size exceeds available storage space";
1340 rs.insert(e.second.shard->index());
1356 if (
auto const it {
shards_.find(shardIndex)};
1357 it !=
shards_.end() && it->second.shard)
1359 shard = it->second.shard;
1367 std::tie(std::ignore, pCache, nCache) = shard->getBackendAll();
1377 return boost::filesystem::space(
dir_).available;
1382 "exception " << e.
what() <<
" in function " << __func__;
1394 if (!shard->store(ledger))
1399 shards_.erase(shard->index());
1407 shard->removeOnDestroy();
1411 else if (shard->isBackendComplete())
1415 if (
auto const it {
shards_.find(shard->index())};
1427 "shard " << shard->index() <<
1428 " is no longer being acquired";
1449 if (section.empty())
1452 return std::make_unique<DatabaseShardImp>(
std::unique_ptr< DatabaseShard > make_ShardStore(Application &app, Stoppable &parent, Scheduler &scheduler, int readThreads, beast::Journal j)
Holds a collection of configuration values.
std::tuple< std::shared_ptr< Ledger >, std::uint32_t, uint256 > loadLedgerHelper(std::string const &sqlSuffix, Application &app, bool acquire)
std::uint32_t ledgersPerShard_
std::uint32_t lastLedgerSeq(std::uint32_t shardIndex) const override
Calculates the last ledger sequence for a given shard index.
std::uint32_t earliestShardIndex() const override
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
boost::optional< std::uint32_t > prepareLedger(std::uint32_t validLedgerSeq) override
Prepare to store a new ledger in the shard being acquired.
bool storeLedger(std::shared_ptr< Ledger const > const &srcLedger) override
Copies a ledger stored in a different database to this one.
Persistency layer for NodeObject.
std::shared_ptr< NodeObject > fetch(uint256 const &hash, std::uint32_t seq) override
Fetch an object.
std::shared_ptr< NodeObject > doFetch(uint256 const &hash, std::uint32_t seq, TaggedCache< uint256, NodeObject > &pCache, KeyCache< uint256 > &nCache, bool isAsync)
std::shared_ptr< Ledger > loadByIndex(std::uint32_t ledgerIndex, Application &app, bool acquire)
std::uint32_t seqToShardIndex(std::uint32_t seq) const override
Calculates the shard index for a given ledger sequence.
Stream trace() const
Severity stream access functions.
std::pair< std::shared_ptr< PCache >, std::shared_ptr< NCache > > getCache(std::uint32_t seq)
void removePreShard(std::uint32_t shardIndex) override
Remove a previously prepared shard index for import.
LedgerIndex getValidLedgerIndex()
static std::string shardDatabase()
NodeObjectType
The types of node objects.
std::enable_if_t<! std::is_void< typename UnaryFunc::return_type >::value, typename UnaryFunc::return_type > foreach(UnaryFunc f)
Visit every active peer and return a value The functor must:
static std::shared_ptr< NodeObject > createObject(NodeObjectType type, Blob &&data, uint256 const &hash)
Create an object from fields.
std::unique_ptr< TaskQueue > taskQueue_
void setStored(std::shared_ptr< Ledger const > const &ledger) override
Notifies the database that the given ledger has been fully acquired and stored.
std::enable_if_t< std::is_integral< Integral >::value &&detail::is_engine< Engine >::value, Integral > rand_int(Engine &engine, Integral min, Integral max)
Return a uniformly distributed random integer.
bool prepareShard(std::uint32_t shardIndex) override
Prepare a shard index to be imported into the database.
constexpr auto kilobytes(T value) noexcept
virtual OperatingMode getOperatingMode() const =0
std::uint64_t available() const
bool asyncFetch(uint256 const &hash, std::uint32_t seq, std::shared_ptr< NodeObject > &object) override
Fetch an object without waiting.
std::string to_string(ListDisposition disposition)
static const uint256 finalKey
virtual NetworkOPs & getOPs()=0
void sweep() override
Remove expired entries from the positive and negative caches.
Sends a message to all peers.
void setParent(Stoppable &parent)
Set the parent of this Stoppable.
static constexpr std::uint32_t version
int getDesiredAsyncReadCount(std::uint32_t seq) override
Get the maximum number of async reads the node store prefers.
std::uint32_t firstLedgerSeq(std::uint32_t shardIndex) const override
Calculates the first ledger sequence for a given shard index.
float getCacheHitRate() override
Get the positive cache hits to total attempts ratio.
std::uint64_t avgShardFileSz_
std::shared_ptr< NodeObject > fetchFrom(uint256 const &hash, std::uint32_t seq) override
std::string getPreShards() override
Get shard indexes being imported.
std::int32_t getWriteLoad() const override
Retrieve the estimated number of pending write operations.
@ DISCONNECTED
not ready to process requests
Provides an interface for starting and stopping.
virtual Family * shardFamily()=0
std::shared_ptr< Shard > shard
bool initConfig(std::lock_guard< std::mutex > &)
static LedgerInfo deserializeHeader(Slice data, bool hasPrefix)
std::string getCompleteShards() override
Query which complete shards are stored.
virtual LedgerMaster & getLedgerMaster()=0
std::map< std::uint32_t, ShardInfo > shards_
virtual std::shared_ptr< NodeObject > fetch(uint256 const &hash, std::uint32_t seq)=0
Fetch an object.
~DatabaseShardImp() override
virtual Config & config()=0
boost::filesystem::path dir_
void updateStatus(std::lock_guard< std::mutex > &)
constexpr std::uint32_t seqToShardIndex(std::uint32_t seq, std::uint32_t ledgersPerShard=DatabaseShard::ledgersPerShardDefault)
virtual std::pair< PublicKey, SecretKey > const & nodeIdentity()=0
A collection of historical shards.
bool importShard(std::uint32_t shardIndex, boost::filesystem::path const &srcDir) override
Import a shard into the shard database.
void storeStats(size_t sz)
A generic endpoint for log messages.
std::uint32_t earliestLedgerSeq() const
std::uint32_t acquireIndex_
Scheduling for asynchronous backend activity.
boost::optional< std::uint32_t > findAcquireIndex(std::uint32_t validLedgerSeq, std::lock_guard< std::mutex > &)
std::shared_ptr< NodeObject > fetchInternal(uint256 const &hash, std::shared_ptr< Backend > backend)
bool init() override
Initialize the database.
void store(NodeObjectType type, Blob &&data, uint256 const &hash, std::uint32_t seq) override
Store the object.
static constexpr auto importMarker_
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
virtual bool storeLedger(std::shared_ptr< Ledger const > const &srcLedger)=0
Copies a ledger stored in a different database to this one.
virtual NodeStore::Database & getNodeStore()=0
void import(Database &source) override
Import the application local node store.
std::shared_ptr< Ledger > fetchLedger(uint256 const &hash, std::uint32_t seq) override
Fetch a ledger from the shard store.
virtual bool asyncFetch(uint256 const &hash, std::uint32_t seq, std::shared_ptr< NodeObject > &object)=0
Fetch an object without waiting.
LedgerIndex getCurrentLedgerIndex()
virtual Overlay & overlay()=0
void validate() override
Verifies shard store data is valid.
bool getHashesByIndex(std::uint32_t ledgerIndex, uint256 &ledgerHash, uint256 &parentHash, Application &app)
boost::icl::interval_set< T, std::less, ClosedInterval< T > > RangeSet
A set of closed intervals over the domain T.
DatabaseShardImp()=delete
static std::string nodeDatabase()
void finalizeShard(ShardInfo &shardInfo, bool writeSQLite, std::lock_guard< std::mutex > &)
void onStop() override
Override called when the stop notification is issued.
std::unique_ptr< nudb::context > ctx_
Section & section(std::string const &name)
Returns the section with the given name.
int add256(uint256 const &)
bool isStopping() const
Returns true if the stoppable should stop.
bool storeLedgerInShard(std::shared_ptr< Shard > &shard, std::shared_ptr< Ledger const > const &ledger)