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/Log.h>
10#include <xrpl/basics/contract.h>
11#include <xrpl/beast/core/CurrentThreadName.h>
12#include <xrpl/beast/utility/Journal.h>
13#include <xrpl/beast/utility/instrumentation.h>
14#include <xrpl/config/BasicConfig.h>
15#include <xrpl/config/Constants.h>
16#include <xrpl/ledger/Ledger.h>
17#include <xrpl/nodestore/Database.h>
18#include <xrpl/nodestore/Scheduler.h>
19#include <xrpl/nodestore/detail/DatabaseRotatingImp.h>
20#include <xrpl/protocol/Protocol.h>
21#include <xrpl/server/NetworkOPs.h>
22#include <xrpl/server/State.h>
23#include <xrpl/shamap/SHAMapMissingNode.h>
24#include <xrpl/shamap/SHAMapTreeNode.h>
26#include <boost/algorithm/string/predicate.hpp>
27#include <boost/filesystem/directory.hpp>
28#include <boost/filesystem/operations.hpp>
29#include <boost/filesystem/path.hpp>
107 Throw<std::runtime_error>(
108 std::string(
"Missing [") + Sections::kNodeDatabase +
"] entry in configuration file");
144 auto const minInterval =
155 "online_delete must not be less than ledger_history "
194 state.
writableDb = writableBackend->getName();
195 state.
archiveDb = archiveBackend->getName();
204 std::move(writableBackend),
205 std::move(archiveBackend),
304 LedgerIndex const validatedSeq = validatedLedger->header().seq;
305 if (lastRotated == 0u)
307 lastRotated = validatedSeq;
308 stateDb_.setLastRotated(lastRotated);
311 bool const readyToRotate = validatedSeq >= lastRotated +
deleteInterval_ &&
317 JLOG(
journal_.warn()) <<
"rotating validatedSeq " << validatedSeq <<
" lastRotated "
320 <<
app_.getOPs().strOperatingMode(
false) <<
" age "
327 JLOG(
journal_.debug()) <<
"copying ledger " << validatedSeq;
332 validatedLedger->stateMap().snapShot(
false)->visitNodes(
337 std::placeholders::_1));
342 <<
"Missing node while copying ledger before rotate: " << e.
what();
350 <<
"copied ledger " << validatedSeq <<
" nodecount " << nodeCount;
352 JLOG(
journal_.debug()) <<
"freshening caches";
357 JLOG(
journal_.debug()) << validatedSeq <<
" freshened caches";
359 JLOG(
journal_.trace()) <<
"Making a new backend";
361 JLOG(
journal_.debug()) << validatedSeq <<
" new backend " << newBackend->getName();
367 lastRotated = validatedSeq;
370 std::move(newBackend),
381 JLOG(
journal_.warn()) <<
"finished rotation " << validatedSeq;
396 if (boost::filesystem::exists(dbPath))
398 if (!boost::filesystem::is_directory(dbPath))
400 journal_.error() <<
"node db path must be a directory. " << dbPath.string();
406 boost::filesystem::create_directories(dbPath);
417 using namespace boost::filesystem;
418 auto const stored{
path(sPath)};
419 if (stored.parent_path() == dbPath)
422 sPath = (dbPath / stored.filename()).string();
433 bool writableDbExists =
false;
434 bool archiveDbExists =
false;
437 for (boost::filesystem::directory_iterator it(dbPath);
438 it != boost::filesystem::directory_iterator();
443 writableDbExists =
true;
447 archiveDbExists =
true;
449 else if (
dbPrefix_.compare(it->path().stem().string()) == 0)
456 (!archiveDbExists && !state.
archiveDb.
empty()) || (writableDbExists != archiveDbExists) ||
461 stateDbPathName +=
"*";
463 journal_.error() <<
"state db error:\n"
464 <<
" writableDbExists " << writableDbExists <<
" archiveDbExists "
465 << archiveDbExists <<
'\n'
466 <<
" writableDb '" << state.
writableDb <<
"' archiveDb '"
468 <<
"The existing data is in a corrupted state.\n"
469 <<
"To resume operation, remove the files matching "
470 << stateDbPathName.
string() <<
" and contents of the directory "
472 <<
"Optionally, you can move those files to another\n"
473 <<
"location if you wish to analyze or back up the data.\n"
474 <<
"However, there is no guarantee that the data in its\n"
475 <<
"existing form is usable.";
481 for (boost::filesystem::path
const& p : pathsToDelete)
482 boost::filesystem::remove_all(p);
489 boost::filesystem::path newPath;
500 newPath = boost::filesystem::unique_path(p);
520 XRPL_ASSERT(
deleteInterval_,
"xrpl::SHAMapStoreImp::clearSql : nonzero delete interval");
524 JLOG(
journal_.trace()) <<
"Begin: Look up lowest value of: " << tableName;
525 auto m = getMinSeq();
526 JLOG(
journal_.trace()) <<
"End: Look up lowest value of: " << tableName;
534 if (min == lastRotated)
537 JLOG(
journal_.trace()) <<
"Nothing to delete from " << tableName;
541 JLOG(
journal_.debug()) <<
"start deleting in: " << tableName <<
" from " << min <<
" to "
543 while (min < lastRotated)
547 <<
" rows with LedgerSeq < " << min <<
" from: " << tableName;
548 deleteBeforeSeq(min);
550 << min <<
" from: " << tableName;
553 if (min < lastRotated)
558 JLOG(
journal_.debug()) <<
"finished deleting from: " << tableName;
586 JLOG(
journal_.trace()) <<
"Begin: Clear internal ledgers up to " << lastRotated;
588 JLOG(
journal_.trace()) <<
"End: Clear internal ledgers up to " << lastRotated;
592 auto& db =
app_.getRelationalDatabase();
598 [&db](
LedgerIndex min) ->
void { db.deleteBeforeLedgerSeq(min); });
602 if (!
app_.config().useTxTables())
609 [&db](
LedgerIndex min) ->
void { db.deleteTransactionsBeforeLedgerSeq(min); });
615 "AccountTransactions",
617 [&db](
LedgerIndex min) ->
void { db.deleteAccountTransactionsBeforeLedgerSeq(min); });
632 <<
"s for node to stabilize. state: "
633 <<
app_.getOPs().strOperatingMode(mode,
false) <<
". age "
634 << age.count() <<
's';
637 mode =
netOPs_->getOperatingMode();
665 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.
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.
static Manager & instance()
Returns the instance of the manager singleton.
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.
Scheduling for asynchronous backend activity.
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::unique_ptr< NodeStore::Backend > makeBackendRotating(std::string path=std::string())
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_
static std::uint32_t const kMinimumDeletionIntervalSa
int fdRequired() const override
Returns the number of file descriptors that are needed.
std::unique_ptr< NodeStore::Database > makeNodeStore(int readThreads) override
std::chrono::milliseconds backOff_
std::atomic< LedgerIndex > canDelete_
NodeStore::DatabaseRotating * dbRotating_
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_
NodeStore::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.
static std::uint32_t const kMinimumDeletionInterval
bool freshenCache(CacheInstance &cache)
void clearPrior(LedgerIndex lastRotated)
SHAMapStoreImp(Application &app, NodeStore::Scheduler &scheduler, beast::Journal journal)
SHAMapHash const & getHash() const
Return the hash of this 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.
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, NodeStore::Scheduler &scheduler, beast::Journal journal)
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