xrpld
Loading...
Searching...
No Matches
SHAMapStoreImp.cpp
1#include <xrpld/app/misc/SHAMapStoreImp.h>
2
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>
7
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>
25
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>
30
31#include <algorithm>
32#include <cstdint>
33#include <functional>
34#include <limits>
35#include <memory>
36#include <mutex>
37#include <optional>
38#include <stdexcept>
39#include <string>
40#include <thread>
41#include <utility>
42#include <vector>
43
44namespace xrpl {
45void
47{
48 std::scoped_lock const lock(mutex);
49 initStateDB(sqlDb, config, dbName);
50}
51
59
62{
63 std::scoped_lock const lock(mutex);
64
65 return xrpl::setCanDelete(sqlDb, canDelete);
66}
67
75
76void
82
83void
89
90//------------------------------------------------------------------------------
91
93 Application& app,
94 NodeStore::Scheduler& scheduler,
95 beast::Journal journal)
96 : app_(app)
97 , scheduler_(scheduler)
98 , journal_(journal)
99 , working_(true)
100 , canDelete_(std::numeric_limits<LedgerIndex>::max())
101{
102 Config& config{app.config()};
103
104 Section& section{config.section(Sections::kNodeDatabase)};
105 if (section.empty())
106 {
107 Throw<std::runtime_error>(
108 std::string("Missing [") + Sections::kNodeDatabase + "] entry in configuration file");
109 }
110
111 // RocksDB only. Use sensible defaults if no values specified.
112 if (boost::iequals(get(section, Keys::kType), "RocksDB"))
113 {
114 if (!section.exists(Keys::kCacheMb))
115 {
116 section.set(
118 }
119
120 if (!section.exists(Keys::kFilterBits) && (config.nodeSize >= 2))
121 section.set(Keys::kFilterBits, "10");
122 }
123
125
126 if (deleteInterval_ != 0u)
127 {
128 // Configuration that affects the behavior of online delete
130 std::uint32_t temp = 0;
131 if (getIfExists(section, Keys::kBackOffMilliseconds, temp) ||
132 // Included for backward compatibility with an undocumented setting
133 getIfExists(section, Keys::kBackOff, temp))
134 {
136 }
137 if (getIfExists(section, Keys::kAgeThresholdSeconds, temp))
139 if (getIfExists(section, Keys::kRecoveryWaitSeconds, temp))
141
143
144 auto const minInterval =
146 if (deleteInterval_ < minInterval)
147 {
149 "online_delete must be at least " + std::to_string(minInterval));
150 }
151
152 if (config.ledgerHistory > deleteInterval_)
153 {
155 "online_delete must not be less than ledger_history "
156 "(currently " +
157 std::to_string(config.ledgerHistory) + ")");
158 }
159
160 stateDb_.init(config, dbName_);
161 dbPaths();
162 }
163}
164
167{
168 auto nscfg = app_.config().section(Sections::kNodeDatabase);
169
170 // Provide default values.
171 if (!nscfg.exists(Keys::kCacheSize))
172 {
173 nscfg.set(
175 std::to_string(app_.config().getValueFor(SizedItem::TreeCacheSize, std::nullopt)));
176 }
177
178 if (!nscfg.exists(Keys::kCacheAge))
179 {
180 nscfg.set(
182 std::to_string(app_.config().getValueFor(SizedItem::TreeCacheAge, std::nullopt)));
183 }
184
186
187 if (deleteInterval_ != 0u)
188 {
189 SavedState state = stateDb_.getState();
190 auto writableBackend = makeBackendRotating(state.writableDb);
191 auto archiveBackend = makeBackendRotating(state.archiveDb);
192 if (state.writableDb.empty())
193 {
194 state.writableDb = writableBackend->getName();
195 state.archiveDb = archiveBackend->getName();
196 stateDb_.setState(state);
197 }
198
199 // Create NodeStore with two backends to allow online deletion of
200 // data
203 readThreads,
204 std::move(writableBackend),
205 std::move(archiveBackend),
206 nscfg,
207 app_.getJournal(kNodeStoreName));
208 fdRequired_ += dbr->fdRequired();
209 dbRotating_ = dbr.get();
210 db.reset(dynamic_cast<NodeStore::Database*>(dbr.release()));
211 }
212 else
213 {
215 megabytes(app_.config().getValueFor(SizedItem::BurstSize, std::nullopt)),
217 readThreads,
218 nscfg,
219 app_.getJournal(kNodeStoreName));
220 fdRequired_ += db->fdRequired();
221 }
222 return db;
223}
224
225void
227{
228 {
229 std::scoped_lock const lock(mutex_);
230 newLedger_ = ledger;
231 working_ = true;
232 }
233 cond_.notify_one();
234}
235
236void
238{
239 if (!working_)
240 return;
241
243 rendezvous_.wait(lock, [&] { return !working_; });
244}
245
246int
248{
249 return fdRequired_;
250}
251
252bool
254{
255 // Copy a single record from node to dbRotating_
256 dbRotating_->fetchNodeObject(
258 if ((++nodeCount % checkHealthInterval_) == 0u)
259 {
261 return false;
262 }
263
264 return true;
265}
266
267void
269{
270 beast::setCurrentThreadName("SHAMapStore");
271 LedgerIndex lastRotated = stateDb_.getState().lastRotated;
272 netOPs_ = &app_.getOPs();
273 ledgerMaster_ = &app_.getLedgerMaster();
274 fullBelowCache_ = &(*app_.getNodeFamily().getFullBelowCache());
275 treeNodeCache_ = &(*app_.getNodeFamily().getTreeNodeCache());
276
277 if (advisoryDelete_)
278 canDelete_ = stateDb_.getCanDelete();
279
280 while (true)
281 {
282 healthy_ = true;
283 std::shared_ptr<Ledger const> validatedLedger;
284
285 {
287 working_ = false;
288 rendezvous_.notify_all();
289 if (stop_)
290 {
291 return;
292 }
293 cond_.wait(lock);
294 if (newLedger_)
295 {
296 validatedLedger = std::move(newLedger_);
297 }
298 else
299 {
300 continue;
301 }
302 }
303
304 LedgerIndex const validatedSeq = validatedLedger->header().seq;
305 if (lastRotated == 0u)
306 {
307 lastRotated = validatedSeq;
308 stateDb_.setLastRotated(lastRotated);
309 }
310
311 bool const readyToRotate = validatedSeq >= lastRotated + deleteInterval_ &&
312 canDelete_ >= lastRotated - 1 && healthWait() == HealthResult::KeepGoing;
313
314 // will delete up to (not including) lastRotated
315 if (readyToRotate)
316 {
317 JLOG(journal_.warn()) << "rotating validatedSeq " << validatedSeq << " lastRotated "
318 << lastRotated << " deleteInterval " << deleteInterval_
319 << " canDelete_ " << canDelete_ << " state "
320 << app_.getOPs().strOperatingMode(false) << " age "
321 << ledgerMaster_->getValidatedLedgerAge().count() << 's';
322
323 clearPrior(lastRotated);
325 return;
326
327 JLOG(journal_.debug()) << "copying ledger " << validatedSeq;
328 std::uint64_t nodeCount = 0;
329
330 try
331 {
332 validatedLedger->stateMap().snapShot(false)->visitNodes(
333 std::bind(
335 this,
336 std::ref(nodeCount),
337 std::placeholders::_1));
338 }
339 catch (SHAMapMissingNode const& e)
340 {
341 JLOG(journal_.error())
342 << "Missing node while copying ledger before rotate: " << e.what();
343 continue;
344 }
345
347 return;
348 // Only log if we completed without a "health" abort
349 JLOG(journal_.debug())
350 << "copied ledger " << validatedSeq << " nodecount " << nodeCount;
351
352 JLOG(journal_.debug()) << "freshening caches";
355 return;
356 // Only log if we completed without a "health" abort
357 JLOG(journal_.debug()) << validatedSeq << " freshened caches";
358
359 JLOG(journal_.trace()) << "Making a new backend";
360 auto newBackend = makeBackendRotating();
361 JLOG(journal_.debug()) << validatedSeq << " new backend " << newBackend->getName();
362
363 clearCaches(validatedSeq);
365 return;
366
367 lastRotated = validatedSeq;
368
369 dbRotating_->rotate(
370 std::move(newBackend),
371 [&](std::string const& writableName, std::string const& archiveName) {
372 SavedState savedState;
373 savedState.writableDb = writableName;
374 savedState.archiveDb = archiveName;
375 savedState.lastRotated = lastRotated;
376 stateDb_.setState(savedState);
377
378 clearCaches(validatedSeq);
379 });
380
381 JLOG(journal_.warn()) << "finished rotation " << validatedSeq;
382 }
383 }
384}
385
386void
388{
389 Section const section{app_.config().section(Sections::kNodeDatabase)};
390
391 // Skip creating the directory when an in-memory database is used.
392 if (boost::iequals(get(section, Keys::kType), "memory"))
393 return;
394
395 boost::filesystem::path dbPath = get(section, Keys::kPath);
396 if (boost::filesystem::exists(dbPath))
397 {
398 if (!boost::filesystem::is_directory(dbPath))
399 {
400 journal_.error() << "node db path must be a directory. " << dbPath.string();
401 Throw<std::runtime_error>("node db path must be a directory.");
402 }
403 }
404 else
405 {
406 boost::filesystem::create_directories(dbPath);
407 }
408
409 SavedState state = stateDb_.getState();
410
411 {
412 auto update = [&dbPath](std::string& sPath) {
413 if (sPath.empty())
414 return false;
415
416 // Check if configured "path" matches stored directory path
417 using namespace boost::filesystem;
418 auto const stored{path(sPath)};
419 if (stored.parent_path() == dbPath)
420 return false;
421
422 sPath = (dbPath / stored.filename()).string();
423 return true;
424 };
425
426 if (update(state.writableDb))
427 {
428 update(state.archiveDb);
429 stateDb_.setState(state);
430 }
431 }
432
433 bool writableDbExists = false;
434 bool archiveDbExists = false;
435
437 for (boost::filesystem::directory_iterator it(dbPath);
438 it != boost::filesystem::directory_iterator();
439 ++it)
440 {
441 if (state.writableDb.compare(it->path().string()) == 0)
442 {
443 writableDbExists = true;
444 }
445 else if (state.archiveDb.compare(it->path().string()) == 0)
446 {
447 archiveDbExists = true;
448 }
449 else if (dbPrefix_.compare(it->path().stem().string()) == 0)
450 {
451 pathsToDelete.push_back(it->path());
452 }
453 }
454
455 if ((!writableDbExists && !state.writableDb.empty()) ||
456 (!archiveDbExists && !state.archiveDb.empty()) || (writableDbExists != archiveDbExists) ||
457 state.writableDb.empty() != state.archiveDb.empty())
458 {
459 boost::filesystem::path stateDbPathName = app_.config().legacy(Sections::kDatabasePath);
460 stateDbPathName /= dbName_;
461 stateDbPathName += "*";
462
463 journal_.error() << "state db error:\n"
464 << " writableDbExists " << writableDbExists << " archiveDbExists "
465 << archiveDbExists << '\n'
466 << " writableDb '" << state.writableDb << "' archiveDb '"
467 << state.archiveDb << "\n\n"
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 "
471 << get(section, Keys::kPath) << '\n'
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.";
476
477 Throw<std::runtime_error>("state db error");
478 }
479
480 // The necessary directories exist. Now, remove any others.
481 for (boost::filesystem::path const& p : pathsToDelete)
482 boost::filesystem::remove_all(p);
483}
484
487{
488 Section section{app_.config().section(Sections::kNodeDatabase)};
489 boost::filesystem::path newPath;
490
491 if (!path.empty())
492 {
493 newPath = path;
494 }
495 else
496 {
497 boost::filesystem::path p = get(section, Keys::kPath);
498 p /= dbPrefix_;
499 p += ".%%%%";
500 newPath = boost::filesystem::unique_path(p);
501 }
502 section.set(Keys::kPath, newPath.string());
503
505 section,
506 megabytes(app_.config().getValueFor(SizedItem::BurstSize, std::nullopt)),
508 app_.getJournal(kNodeStoreName))};
509 backend->open();
510 return backend;
511}
512
513void
515 LedgerIndex lastRotated,
516 std::string const& tableName,
517 std::function<std::optional<LedgerIndex>()> const& getMinSeq,
518 std::function<void(LedgerIndex)> const& deleteBeforeSeq)
519{
520 XRPL_ASSERT(deleteInterval_, "xrpl::SHAMapStoreImp::clearSql : nonzero delete interval");
522
523 {
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;
527 if (!m)
528 return;
529 min = *m;
530 }
531
532 if (min > lastRotated || healthWait() == HealthResult::Stopping)
533 return;
534 if (min == lastRotated)
535 {
536 // Micro-optimization mainly to clarify logs
537 JLOG(journal_.trace()) << "Nothing to delete from " << tableName;
538 return;
539 }
540
541 JLOG(journal_.debug()) << "start deleting in: " << tableName << " from " << min << " to "
542 << lastRotated;
543 while (min < lastRotated)
544 {
545 min = std::min(lastRotated, min + deleteBatch_);
546 JLOG(journal_.trace()) << "Begin: Delete up to " << deleteBatch_
547 << " rows with LedgerSeq < " << min << " from: " << tableName;
548 deleteBeforeSeq(min);
549 JLOG(journal_.trace()) << "End: Delete up to " << deleteBatch_ << " rows with LedgerSeq < "
550 << min << " from: " << tableName;
552 return;
553 if (min < lastRotated)
556 return;
557 }
558 JLOG(journal_.debug()) << "finished deleting from: " << tableName;
559}
560
561void
563{
564 ledgerMaster_->clearLedgerCachePrior(validatedSeq);
565 // Also clear the FullBelowCache so its generation counter is bumped.
566 // This prevents stale "full below" markers from persisting across
567 // backend rotation/online deletion and interfering with SHAMap sync.
568 fullBelowCache_->clear();
569}
570
571void
573{
575 return;
576 if (freshenCache(app_.getMasterTransaction().getCache()))
577 return;
578}
579
580void
582{
583 // Do not allow ledgers to be acquired from the network
584 // that are about to be deleted.
585 minimumOnline_ = lastRotated + 1;
586 JLOG(journal_.trace()) << "Begin: Clear internal ledgers up to " << lastRotated;
587 ledgerMaster_->clearPriorLedgers(lastRotated);
588 JLOG(journal_.trace()) << "End: Clear internal ledgers up to " << lastRotated;
590 return;
591
592 auto& db = app_.getRelationalDatabase();
593
594 clearSql(
595 lastRotated,
596 "Ledgers",
597 [&db]() -> std::optional<LedgerIndex> { return db.getMinLedgerSeq(); },
598 [&db](LedgerIndex min) -> void { db.deleteBeforeLedgerSeq(min); });
600 return;
601
602 if (!app_.config().useTxTables())
603 return;
604
605 clearSql(
606 lastRotated,
607 "Transactions",
608 [&db]() -> std::optional<LedgerIndex> { return db.getTransactionsMinLedgerSeq(); },
609 [&db](LedgerIndex min) -> void { db.deleteTransactionsBeforeLedgerSeq(min); });
611 return;
612
613 clearSql(
614 lastRotated,
615 "AccountTransactions",
616 [&db]() -> std::optional<LedgerIndex> { return db.getAccountTransactionsMinLedgerSeq(); },
617 [&db](LedgerIndex min) -> void { db.deleteAccountTransactionsBeforeLedgerSeq(min); });
619 return;
620}
621
624{
625 auto age = ledgerMaster_->getValidatedLedgerAge();
626 OperatingMode mode = netOPs_->getOperatingMode();
628 while (!stop_ && (mode != OperatingMode::FULL || age > ageThreshold_))
629 {
630 lock.unlock();
631 JLOG(journal_.warn()) << "Waiting " << recoveryWaitTime_.count()
632 << "s for node to stabilize. state: "
633 << app_.getOPs().strOperatingMode(mode, false) << ". age "
634 << age.count() << 's';
636 age = ledgerMaster_->getValidatedLedgerAge();
637 mode = netOPs_->getOperatingMode();
638 lock.lock();
639 }
640
642}
643
644void
646{
647 if (thread_.joinable())
648 {
649 {
650 std::scoped_lock const lock(mutex_);
651 stop_ = true;
652 cond_.notify_one();
653 }
654 thread_.join();
655 }
656}
657
660{
661 // minimumOnline_ with 0 value is equivalent to unknown/not set.
662 // Don't attempt to acquire ledgers if that value is unknown.
663 if ((deleteInterval_ != 0u) && (minimumOnline_ != 0u))
664 return minimumOnline_.load();
665 return app_.getLedgerMaster().minSqlSeq();
666}
667
668//------------------------------------------------------------------------------
669
672{
673 return std::make_unique<SHAMapStoreImp>(app, scheduler, journal);
674}
675
676} // namespace xrpl
T bind(T... args)
A generic endpoint for log messages.
Definition Journal.h:38
virtual Config & config()=0
Holds unparsed configuration information.
Section & section(std::string const &name)
Returns the section with the given name.
bool standalone() const
Definition Config.h:316
std::size_t nodeSize
Definition Config.h:198
std::uint32_t ledgerHistory
Definition Config.h:192
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.
Definition Config.cpp:1190
Persistency layer for NodeObject.
Definition Database.h:32
virtual std::unique_ptr< Backend > makeBackend(Section const &parameters, 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
Definition SHAMapHash.h:24
void setState(SavedState const &state)
void init(BasicConfig const &config, std::string const &dbName)
LedgerIndex setCanDelete(LedgerIndex canDelete)
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.
Definition BasicConfig.h:24
bool empty() const
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.
T compare(T... args)
T empty(T... args)
T make_unique(T... args)
T max(T... args)
T min(T... args)
void setCurrentThreadName(std::string_view newThreadName)
Changes the name of the caller thread.
STL namespace.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
void initStateDB(soci::session &session, BasicConfig const &config, std::string const &dbName)
initStateDB Opens a session with the State database.
Definition State.cpp:21
void setSavedState(soci::session &session, SavedState const &state)
setSavedState Saves the given state.
Definition State.cpp:98
std::uint32_t LedgerIndex
A ledger index.
Definition Protocol.h:259
T get(Section const &section, 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.
Definition State.cpp:109
std::unique_ptr< SHAMapStore > makeSHAMapStore(Application &app, NodeStore::Scheduler &scheduler, beast::Journal journal)
bool getIfExists(Section const &section, std::string const &name, T &v)
SavedState getSavedState(soci::session &session)
getSavedState Returns the saved state.
Definition State.cpp:87
constexpr auto megabytes(T value) noexcept
LedgerIndex setCanDelete(soci::session &session, LedgerIndex canDelete)
setCanDelete Updates the ledger sequence which can be deleted.
Definition State.cpp:79
OperatingMode
Specifies the mode under which the server believes it's operating.
Definition NetworkOPs.h:50
@ FULL
we have the ledger and can even validate
Definition NetworkOPs.h:55
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:49
LedgerIndex getCanDelete(soci::session &session)
getCanDelete Returns the ledger sequence which can be deleted.
Definition State.cpp:70
T push_back(T... args)
T ref(T... args)
T reset(T... args)
T sleep_for(T... args)
static constexpr auto kBackOffMilliseconds
Definition Constants.h:91
static constexpr auto kAdvisoryDelete
Definition Constants.h:88
static constexpr auto kCacheMb
Definition Constants.h:97
static constexpr auto kCacheSize
Definition Constants.h:98
static constexpr auto kBackOff
Definition Constants.h:90
static constexpr auto kAgeThresholdSeconds
Definition Constants.h:89
static constexpr auto kDeleteBatch
Definition Constants.h:103
static constexpr auto kFilterBits
Definition Constants.h:108
static constexpr auto kRecoveryWaitSeconds
Definition Constants.h:145
static constexpr auto kType
Definition Constants.h:170
static constexpr auto kPath
Definition Constants.h:140
static constexpr auto kCacheAge
Definition Constants.h:96
static constexpr auto kOnlineDelete
Definition Constants.h:133
std::string writableDb
Definition State.h:13
LedgerIndex lastRotated
Definition State.h:15
std::string archiveDb
Definition State.h:14
static constexpr auto kNodeDatabase
Definition Constants.h:31
static constexpr auto kDatabasePath
Definition Constants.h:13
T to_string(T... args)
T what(T... args)