1#include <xrpld/app/misc/SHAMapStoreImp.h>
3#include <xrpld/app/ledger/TransactionMaster.h>
4#include <xrpld/app/misc/SHAMapStore.h>
5#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
6#include <xrpld/core/Config.h>
8#include <xrpl/basics/ByteUtilities.h>
9#include <xrpl/basics/FileUtilities.h>
10#include <xrpl/basics/Log.h>
11#include <xrpl/basics/contract.h>
12#include <xrpl/beast/core/CurrentThreadName.h>
13#include <xrpl/beast/utility/Journal.h>
14#include <xrpl/beast/utility/instrumentation.h>
15#include <xrpl/config/BasicConfig.h>
16#include <xrpl/config/Constants.h>
17#include <xrpl/ledger/Ledger.h>
18#include <xrpl/nodestore/Database.h>
19#include <xrpl/nodestore/Manager.h>
20#include <xrpl/nodestore/NodeObject.h>
21#include <xrpl/nodestore/Scheduler.h>
22#include <xrpl/nodestore/detail/DatabaseRotatingImp.h>
23#include <xrpl/protocol/Protocol.h>
24#include <xrpl/protocol/Serializer.h>
25#include <xrpl/server/NetworkOPs.h>
26#include <xrpl/server/State.h>
27#include <xrpl/shamap/SHAMapMissingNode.h>
28#include <xrpl/shamap/SHAMapTreeNode.h>
30#include <boost/algorithm/string/predicate.hpp>
109 Throw<std::runtime_error>(
110 std::string(
"Missing [") + Sections::kNodeDatabase +
"] entry in configuration file");
146 auto const minInterval =
157 "online_delete must not be less than ledger_history "
196 state.
writableDb = writableBackend->getName();
197 state.
archiveDb = archiveBackend->getName();
206 std::move(writableBackend),
207 std::move(archiveBackend),
262 XRPL_ASSERT(node.
cowid() == 0,
"SHAMapStoreImp::copyNode : rescued node must be clean");
273 JLOG(
journal_.warn()) <<
"copyNode: re-stored node missing from both backends, hash="
274 << hash <<
" type=" <<
static_cast<int>(node.
getType());
322 LedgerIndex const validatedSeq = validatedLedger->header().seq;
323 if (lastRotated == 0u)
325 lastRotated = validatedSeq;
326 stateDb_.setLastRotated(lastRotated);
329 bool const readyToRotate = validatedSeq >= lastRotated +
deleteInterval_ &&
335 JLOG(
journal_.warn()) <<
"rotating validatedSeq " << validatedSeq <<
" lastRotated "
338 <<
app_.getOPs().strOperatingMode(
false) <<
" age "
345 JLOG(
journal_.debug()) <<
"copying ledger " << validatedSeq;
350 validatedLedger->stateMap().snapShot(
false)->visitNodes(
358 <<
"Missing node while copying ledger before rotate: " << e.
what();
366 <<
"copied ledger " << validatedSeq <<
" nodecount " << nodeCount;
374 struct RotationExposureGuard
377 ~RotationExposureGuard()
382 RotationExposureGuard
const rotationExposureGuard{*
dbRotating_};
385 JLOG(
journal_.debug()) <<
"freshening caches";
390 JLOG(
journal_.debug()) << validatedSeq <<
" freshened caches";
392 JLOG(
journal_.trace()) <<
"Making a new backend";
394 JLOG(
journal_.debug()) << validatedSeq <<
" new backend " << newBackend->getName();
400 lastRotated = validatedSeq;
403 std::move(newBackend),
414 JLOG(
journal_.warn()) <<
"finished rotation " << validatedSeq;
433 journal_.error() <<
"node db path must be a directory. " << dbPath.
string();
452 if (stored.parent_path() == dbPath)
455 sPath = (dbPath / stored.
filename()).string();
466 bool writableDbExists =
false;
467 bool archiveDbExists =
false;
476 writableDbExists =
true;
480 archiveDbExists =
true;
482 else if (
dbPrefix_ == it->path().stem().string())
489 (!archiveDbExists && !state.
archiveDb.
empty()) || (writableDbExists != archiveDbExists) ||
494 stateDbPathName +=
"*";
496 journal_.error() <<
"state db error:\n"
497 <<
" writableDbExists " << writableDbExists <<
" archiveDbExists "
498 << archiveDbExists <<
'\n'
499 <<
" writableDb '" << state.
writableDb <<
"' archiveDb '"
501 <<
"The existing data is in a corrupted state.\n"
502 <<
"To resume operation, remove the files matching "
503 << stateDbPathName.
string() <<
" and contents of the directory "
505 <<
"Optionally, you can move those files to another\n"
506 <<
"location if you wish to analyze or back up the data.\n"
507 <<
"However, there is no guarantee that the data in its\n"
508 <<
"existing form is usable.";
550 XRPL_ASSERT(
deleteInterval_,
"xrpl::SHAMapStoreImp::clearSql : nonzero delete interval");
554 JLOG(
journal_.trace()) <<
"Begin: Look up lowest value of: " << tableName;
555 auto m = getMinSeq();
556 JLOG(
journal_.trace()) <<
"End: Look up lowest value of: " << tableName;
564 if (
min == lastRotated)
567 JLOG(
journal_.trace()) <<
"Nothing to delete from " << tableName;
571 JLOG(
journal_.debug()) <<
"start deleting in: " << tableName <<
" from " <<
min <<
" to "
573 while (
min < lastRotated)
577 <<
" rows with LedgerSeq < " <<
min <<
" from: " << tableName;
578 deleteBeforeSeq(
min);
580 <<
min <<
" from: " << tableName;
583 if (
min < lastRotated)
588 JLOG(
journal_.debug()) <<
"finished deleting from: " << tableName;
616 JLOG(
journal_.trace()) <<
"Begin: Clear internal ledgers up to " << lastRotated;
618 JLOG(
journal_.trace()) <<
"End: Clear internal ledgers up to " << lastRotated;
622 auto& db =
app_.getRelationalDatabase();
632 if (!
app_.config().useTxTables())
645 "AccountTransactions",
647 [&db](
LedgerIndex min) ->
void { db.deleteAccountTransactionsBeforeLedgerSeq(
min); });
662 <<
"s for node to stabilize. state: "
663 <<
app_.getOPs().strOperatingMode(mode,
false) <<
". age "
664 << age.count() <<
's';
667 mode =
netOPs_->getOperatingMode();
695 return app_.getLedgerMaster().minSqlSeq();
A generic endpoint for log messages.
virtual Config & config()=0
Holds unparsed configuration information.
Section & section(std::string const &name)
Returns the section with the given name.
std::uint32_t ledgerHistory
int getValueFor(SizedItem item, std::optional< std::size_t > node=std::nullopt) const
Retrieve the default value for the item at the specified node size.
uint256 const & asUInt256() const
void setState(SavedState const &state)
void init(BasicConfig const &config, std::string const &dbName)
LedgerIndex setCanDelete(LedgerIndex canDelete)
void setLastRotated(LedgerIndex seq)
LedgerIndex getCanDelete()
bool copyNode(std::uint64_t &nodeCount, SHAMapTreeNode const &node)
std::atomic< bool > working_
std::condition_variable cond_
std::uint32_t deleteBatch_
std::atomic< LedgerIndex > minimumOnline_
std::chrono::seconds recoveryWaitTime_
If the node is out of sync during an online_delete healthWait() call, sleep the thread for this time,...
FullBelowCache * fullBelowCache_
std::optional< LedgerIndex > minimumOnline() const override
The minimum ledger to try and maintain in our database.
TreeNodeCache * treeNodeCache_
std::uint32_t deleteInterval_
std::unique_ptr< node_store::Database > makeNodeStore(int readThreads) override
static std::uint32_t const kMinimumDeletionIntervalSa
int fdRequired() const override
Returns the number of file descriptors that are needed.
std::chrono::milliseconds backOff_
std::atomic< LedgerIndex > canDelete_
std::shared_ptr< Ledger const > newLedger_
std::string const dbName_
void clearSql(LedgerIndex lastRotated, std::string const &tableName, std::function< std::optional< LedgerIndex >()> const &getMinSeq, std::function< void(LedgerIndex)> const &deleteBeforeSeq)
delete from sqlite table in batches to not lock the db excessively.
std::condition_variable rendezvous_
std::uint64_t const checkHealthInterval_
HealthResult healthWait()
void clearCaches(LedgerIndex validatedSeq)
std::chrono::seconds ageThreshold_
void rendezvous() const override
LedgerMaster * ledgerMaster_
beast::Journal const journal_
static constexpr auto kNodeStoreName
std::string const dbPrefix_
node_store::Scheduler & scheduler_
HealthResult
This is a health check for online deletion that waits until xrpld is stable before returning.
void onLedgerClosed(std::shared_ptr< Ledger const > const &ledger) override
Called by LedgerMaster every time a ledger validates.
node_store::DatabaseRotating * dbRotating_
std::unique_ptr< node_store::Backend > makeBackendRotating(std::string path=std::string())
static std::uint32_t const kMinimumDeletionInterval
SHAMapStoreImp(Application &app, node_store::Scheduler &scheduler, beast::Journal journal)
bool freshenCache(CacheInstance &cache)
void clearPrior(LedgerIndex lastRotated)
SHAMapHash const & getHash() const
Return the hash of this node.
virtual void serializeWithPrefix(Serializer &) const =0
Serialize the node in a format appropriate for hashing.
virtual SHAMapNodeType getType() const =0
Determines the type of node.
Holds a collection of configuration values.
void set(std::string const &key, std::string const &value)
Set a key/value pair.
bool exists(std::string const &name) const
Returns true if a key with the given name exists.
virtual void setRotationInFlight(bool inFlight)=0
Marks an online-delete rotation as in progress (or completed).
Persistency layer for NodeObject.
virtual std::unique_ptr< Backend > makeBackend(Section const ¶meters, std::size_t burstSize, Scheduler &scheduler, beast::Journal journal)=0
Create a backend.
virtual std::unique_ptr< Database > makeDatabase(std::size_t burstSize, Scheduler &scheduler, int readThreads, Section const &backendParameters, beast::Journal journal)=0
Construct a NodeStore database.
static Manager & instance()
Returns the instance of the manager singleton.
Scheduling for asynchronous backend activity.
T create_directories(T... args)
std::uint32_t cowid() const
Returns the SHAMap that owns this node.
T is_directory(T... args)
void setCurrentThreadName(std::string_view newThreadName)
Changes the name of the caller thread.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
void initStateDB(soci::session &session, BasicConfig const &config, std::string const &dbName)
initStateDB Opens a session with the State database.
void setSavedState(soci::session &session, SavedState const &state)
setSavedState Saves the given state.
std::uint32_t LedgerIndex
A ledger index.
T get(Section const §ion, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
void setLastRotated(soci::session &session, LedgerIndex seq)
setLastRotated Updates the last rotated ledger sequence.
std::unique_ptr< SHAMapStore > makeSHAMapStore(Application &app, node_store::Scheduler &scheduler, beast::Journal journal)
std::filesystem::path uniqueRandomPath(std::filesystem::path const &base, std::string const &prefix="", std::size_t maxAttempts=100)
Generate a unique, non-existing path under base whose filename starts with prefix and ends with a ran...
bool getIfExists(Section const §ion, std::string const &name, T &v)
SavedState getSavedState(soci::session &session)
getSavedState Returns the saved state.
constexpr auto megabytes(T value) noexcept
LedgerIndex setCanDelete(soci::session &session, LedgerIndex canDelete)
setCanDelete Updates the ledger sequence which can be deleted.
OperatingMode
Specifies the mode under which the server believes it's operating.
@ FULL
we have the ledger and can even validate
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
LedgerIndex getCanDelete(soci::session &session)
getCanDelete Returns the ledger sequence which can be deleted.
static constexpr auto kBackOffMilliseconds
static constexpr auto kAdvisoryDelete
static constexpr auto kCacheMb
static constexpr auto kCacheSize
static constexpr auto kBackOff
static constexpr auto kAgeThresholdSeconds
static constexpr auto kDeleteBatch
static constexpr auto kFilterBits
static constexpr auto kRecoveryWaitSeconds
static constexpr auto kType
static constexpr auto kPath
static constexpr auto kCacheAge
static constexpr auto kOnlineDelete
static constexpr auto kNodeDatabase
static constexpr auto kDatabasePath