3#include "data/BackendInterface.hpp"
5#include "data/LedgerCacheInterface.hpp"
6#include "data/LedgerHeaderCache.hpp"
7#include "data/Types.hpp"
8#include "data/cassandra/Concepts.hpp"
9#include "data/cassandra/Handle.hpp"
10#include "data/cassandra/Types.hpp"
11#include "data/cassandra/impl/ExecutionStrategy.hpp"
12#include "util/Assert.hpp"
13#include "util/LedgerUtils.hpp"
14#include "util/Profiler.hpp"
15#include "util/log/Logger.hpp"
17#include <boost/asio/spawn.hpp>
18#include <boost/json/object.hpp>
19#include <boost/uuid/string_generator.hpp>
20#include <boost/uuid/uuid.hpp>
22#include <fmt/format.h>
23#include <xrpl/basics/Blob.h>
24#include <xrpl/basics/base_uint.h>
25#include <xrpl/basics/strHex.h>
26#include <xrpl/protocol/AccountID.h>
27#include <xrpl/protocol/Indexes.h>
28#include <xrpl/protocol/LedgerHeader.h>
29#include <xrpl/protocol/nft.h>
45class CacheBackendCassandraTest;
68 SettingsProviderType settingsProvider_;
70 std::atomic_uint32_t ledgerSequence_ = 0u;
71 friend class ::CacheBackendCassandraTest;
76 mutable ExecutionStrategyType executor_;
78 mutable FetchLedgerCacheType ledgerCache_{};
80 static constexpr std::size_t kTransactionCursorBindIndex = 1;
81 static constexpr std::size_t kTransactionLimitBindIndex = 2;
82 static constexpr std::size_t kMPTokenIssuanceTxCursorBindIndex = 1;
83 static constexpr std::size_t kMPTokenIssuanceTxLimitBindIndex = 2;
84 static constexpr std::size_t kAccountMPTokenIssuanceTxCursorBindIndex = 2;
85 static constexpr std::size_t kAccountMPTokenIssuanceTxLimitBindIndex = 3;
96 SettingsProviderType settingsProvider,
101 , settingsProvider_{std::move(settingsProvider)}
102 , schema_{settingsProvider_}
103 , handle_{settingsProvider_.getSettings()}
104 , executor_{settingsProvider_.getSettings(), handle_}
106 if (
auto const res = handle_.connect(); not res.has_value())
107 throw std::runtime_error(
"Could not connect to database: " + res.error());
110 if (
auto const res = handle_.execute(schema_.createKeyspace); not res.has_value()) {
113 if (res.error().code() != CASS_ERROR_SERVER_UNAUTHORIZED)
114 throw std::runtime_error(
"Could not create keyspace: " + res.error());
117 if (
auto const res = handle_.executeEach(schema_.createSchema); not res.has_value())
118 throw std::runtime_error(
"Could not create schema: " + res.error());
122 schema_.prepareStatements(handle_);
123 }
catch (std::runtime_error
const& ex) {
124 auto const error = fmt::format(
125 "Failed to prepare the statements: {}; readOnly: {}. ReadOnly should be turned off "
127 "node with write access to DB should be started first.",
131 LOG(log_.error()) << error;
132 throw std::runtime_error(error);
134 LOG(log_.info()) <<
"Created (revamped) CassandraBackend";
145 [[nodiscard]] std::chrono::milliseconds
148 return settingsProvider_.getInitialRetryDelay();
154 [[nodiscard]] std::chrono::milliseconds
157 return settingsProvider_.getMaxRetryDelay();
162 xrpl::AccountID
const& account,
163 std::uint32_t
const limit,
165 std::optional<TransactionsCursor>
const& txnCursor,
166 boost::asio::yield_context yield
171 return {.txns = {}, .cursor = {}};
173 Statement
const statement = [
this, forward, &account]() {
175 return schema_->selectAccountTxForward.bind(account);
177 return schema_->selectAccountTx.bind(account);
180 auto cursor = txnCursor;
182 statement.
bindAt(kTransactionCursorBindIndex, cursor->asTuple());
183 LOG(log_.debug()) <<
"account = " << xrpl::strHex(account)
184 <<
" tuple = " << cursor->ledgerSequence << cursor->transactionIndex;
186 auto const seq = forward ? rng->minSequence : rng->maxSequence;
187 auto const placeHolder = forward ? 0u : std::numeric_limits<std::uint32_t>::max();
190 kTransactionCursorBindIndex, std::make_tuple(placeHolder, placeHolder)
192 LOG(log_.debug()) <<
"account = " << xrpl::strHex(account) <<
" idx = " << seq
193 <<
" tuple = " << placeHolder;
199 statement.
bindAt(kTransactionLimitBindIndex,
Limit{limit});
200 auto const res = executor_.read(yield, statement);
201 auto const& results = res.value();
202 if (not results.hasRows()) {
203 LOG(log_.debug()) <<
"No rows returned";
207 std::vector<xrpl::uint256> hashes = {};
208 auto numRows = results.numRows();
209 LOG(log_.info()) <<
"num_rows = " << numRows;
211 for (
auto [hash,
data] :
extract<xrpl::uint256, std::tuple<uint32_t, uint32_t>>(results)) {
212 hashes.push_back(hash);
213 if (--numRows == 0) {
214 LOG(log_.debug()) <<
"Setting cursor";
220 LOG(log_.debug()) <<
"Txns = " << txns.size();
222 if (txns.size() == limit) {
223 LOG(log_.debug()) <<
"Returning cursor";
224 return {txns, cursor};
237 writeLedger(xrpl::LedgerHeader
const& ledgerHeader, std::string&& blob)
override
239 executor_.write(schema_->insertLedgerHeader, ledgerHeader.seq, std::move(blob));
241 executor_.write(schema_->insertLedgerHash, ledgerHeader.hash, ledgerHeader.seq);
243 ledgerSequence_ = ledgerHeader.seq;
246 std::optional<std::uint32_t>
249 if (
auto const res = executor_.read(yield, schema_->selectLatestLedger); res.has_value()) {
250 if (
auto const& rows = *res; rows) {
251 if (
auto const maybeRow = rows.template get<uint32_t>(); maybeRow.has_value())
254 LOG(log_.error()) <<
"Could not fetch latest ledger - no rows";
258 LOG(log_.error()) <<
"Could not fetch latest ledger - no result";
260 LOG(log_.error()) <<
"Could not fetch latest ledger: " << res.error();
266 std::optional<xrpl::LedgerHeader>
268 std::uint32_t
const sequence,
269 boost::asio::yield_context yield
272 if (
auto const lock = ledgerCache_.get(); lock.has_value() && lock->seq == sequence)
275 auto const res = executor_.read(yield, schema_->selectLedgerBySeq, sequence);
277 if (
auto const& result = res.value(); result) {
278 if (
auto const maybeValue = result.template get<std::vector<unsigned char>>();
285 LOG(log_.error()) <<
"Could not fetch ledger by sequence - no rows";
289 LOG(log_.error()) <<
"Could not fetch ledger by sequence - no result";
291 LOG(log_.error()) <<
"Could not fetch ledger by sequence: " << res.error();
297 std::optional<xrpl::LedgerHeader>
300 if (
auto const res = executor_.read(yield, schema_->selectLedgerByHash, hash); res) {
301 if (
auto const& result = res.value(); result) {
302 if (
auto const maybeValue = result.template get<uint32_t>(); maybeValue)
305 LOG(log_.error()) <<
"Could not fetch ledger by hash - no rows";
309 LOG(log_.error()) <<
"Could not fetch ledger by hash - no result";
311 LOG(log_.error()) <<
"Could not fetch ledger by hash: " << res.error();
317 std::optional<LedgerRange>
320 auto const res = executor_.read(yield, schema_->selectLedgerRange);
322 auto const& results = res.value();
323 if (not results.hasRows()) {
324 LOG(log_.debug()) <<
"Could not fetch ledger range - no rows";
335 range.maxSequence = range.minSequence = seq;
336 }
else if (idx == 1) {
337 range.maxSequence = seq;
343 if (range.minSequence > range.maxSequence)
344 std::swap(range.minSequence, range.maxSequence);
346 LOG(log_.debug()) <<
"After hardFetchLedgerRange range is " << range.minSequence <<
":"
347 << range.maxSequence;
350 LOG(log_.error()) <<
"Could not fetch ledger range: " << res.error();
355 std::vector<TransactionAndMetadata>
357 std::uint32_t
const ledgerSequence,
358 boost::asio::yield_context yield
365 std::vector<xrpl::uint256>
367 std::uint32_t
const ledgerSequence,
368 boost::asio::yield_context yield
371 auto start = std::chrono::system_clock::now();
373 executor_.read(yield, schema_->selectAllTransactionHashesInLedger, ledgerSequence);
376 LOG(log_.error()) <<
"Could not fetch all transaction hashes: " << res.error();
380 auto const& result = res.value();
381 if (not result.hasRows()) {
382 LOG(log_.warn()) <<
"Could not fetch all transaction hashes - no rows; ledger = "
383 << std::to_string(ledgerSequence);
387 std::vector<xrpl::uint256> hashes;
389 hashes.push_back(std::move(hash));
391 auto end = std::chrono::system_clock::now();
395 << hashes.size() <<
" transaction hashes from database in "
396 << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
404 xrpl::uint256
const& tokenID,
405 std::uint32_t
const ledgerSequence,
406 boost::asio::yield_context yield
409 auto const res = executor_.read(yield, schema_->selectNFT, tokenID, ledgerSequence);
413 if (
auto const maybeRow = res->template get<uint32_t, xrpl::AccountID, bool>(); maybeRow) {
414 auto [seq, owner, isBurned] = *maybeRow;
415 auto result = std::make_optional<NFT>(tokenID, seq, owner, isBurned);
428 auto uriRes = executor_.read(yield, schema_->selectNFTURI, tokenID, ledgerSequence);
430 if (
auto const maybeUri = uriRes->template get<xrpl::Blob>(); maybeUri)
431 result->uri = *maybeUri;
437 LOG(log_.error()) <<
"Could not fetch NFT - no rows";
443 xrpl::uint256
const& tokenID,
444 std::uint32_t
const limit,
446 std::optional<TransactionsCursor>
const& cursorIn,
447 boost::asio::yield_context yield
452 return {.txns = {}, .cursor = {}};
454 Statement
const statement = [
this, forward, &tokenID]() {
456 return schema_->selectNFTTxForward.bind(tokenID);
458 return schema_->selectNFTTx.bind(tokenID);
461 auto cursor = cursorIn;
463 statement.
bindAt(kTransactionCursorBindIndex, cursor->asTuple());
464 LOG(log_.debug()) <<
"token_id = " << xrpl::strHex(tokenID)
465 <<
" tuple = " << cursor->ledgerSequence << cursor->transactionIndex;
467 auto const seq = forward ? rng->minSequence : rng->maxSequence;
468 auto const placeHolder = forward ? 0 : std::numeric_limits<std::uint32_t>::max();
471 kTransactionCursorBindIndex, std::make_tuple(placeHolder, placeHolder)
473 LOG(log_.debug()) <<
"token_id = " << xrpl::strHex(tokenID) <<
" idx = " << seq
474 <<
" tuple = " << placeHolder;
477 statement.
bindAt(kTransactionLimitBindIndex,
Limit{limit});
479 auto const res = executor_.read(yield, statement);
480 auto const& results = res.value();
481 if (not results.hasRows()) {
482 LOG(log_.debug()) <<
"No rows returned";
486 std::vector<xrpl::uint256> hashes = {};
487 auto numRows = results.numRows();
488 LOG(log_.info()) <<
"num_rows = " << numRows;
490 for (
auto [hash,
data] :
extract<xrpl::uint256, std::tuple<uint32_t, uint32_t>>(results)) {
491 hashes.push_back(hash);
492 if (--numRows == 0) {
493 LOG(log_.debug()) <<
"Setting cursor";
499 ++cursor->transactionIndex;
504 LOG(log_.debug()) <<
"NFT Txns = " << txns.size();
506 if (txns.size() == limit) {
507 LOG(log_.debug()) <<
"Returning cursor";
508 return {txns, cursor};
516 xrpl::uint192
const& mptIssuanceID,
517 std::uint32_t
const limit,
519 std::optional<TransactionsCursor>
const& cursorIn,
520 boost::asio::yield_context yield
523 auto const statement = [
this, forward, &mptIssuanceID]() {
525 return schema_->selectMPTokenIssuanceTxForward.bind(mptIssuanceID);
527 return schema_->selectMPTokenIssuanceTx.bind(mptIssuanceID);
531 kMPTokenIssuanceTxCursorBindIndex,
532 kMPTokenIssuanceTxLimitBindIndex,
542 xrpl::uint192
const& mptIssuanceID,
543 xrpl::AccountID
const& account,
544 std::uint32_t
const limit,
546 std::optional<TransactionsCursor>
const& cursorIn,
547 boost::asio::yield_context yield
550 auto const statement = [
this, forward, &mptIssuanceID, &account]() {
552 return schema_->selectAccountMPTokenIssuanceTxForward.bind(mptIssuanceID, account);
554 return schema_->selectAccountMPTokenIssuanceTx.bind(mptIssuanceID, account);
558 kAccountMPTokenIssuanceTxCursorBindIndex,
559 kAccountMPTokenIssuanceTxLimitBindIndex,
569 xrpl::uint192
const& mptID,
570 std::uint32_t
const limit,
571 std::optional<xrpl::AccountID>
const& cursorIn,
572 std::uint32_t
const ledgerSequence,
573 boost::asio::yield_context yield
576 auto const holderEntries = executor_.read(
578 schema_->selectMPTHolders,
580 cursorIn.value_or(xrpl::AccountID(0)),
584 auto const& holderResults = holderEntries.value();
585 if (not holderResults.hasRows()) {
586 LOG(log_.debug()) <<
"No rows returned";
590 std::vector<xrpl::uint256> mptKeys;
591 std::optional<xrpl::AccountID> cursor;
593 mptKeys.push_back(xrpl::keylet::mptoken(mptID, holder).key);
599 auto it = std::remove_if(mptObjects.begin(), mptObjects.end(), [](Blob
const& mpt) {
603 mptObjects.erase(it, mptObjects.end());
605 ASSERT(mptKeys.size() <= limit,
"Number of keys can't exceed the limit");
606 if (mptKeys.size() == limit)
607 return {mptObjects, cursor};
609 return {mptObjects, {}};
614 xrpl::uint256
const& key,
615 std::uint32_t
const sequence,
616 boost::asio::yield_context yield
619 LOG(log_.debug()) <<
"Fetching ledger object for seq " << sequence
620 <<
", key = " << xrpl::to_string(key);
621 if (
auto const res = executor_.read(yield, schema_->selectObject, key, sequence); res) {
622 if (
auto const result = res->template get<Blob>(); result) {
626 LOG(log_.debug()) <<
"Could not fetch ledger object - no rows";
629 LOG(log_.error()) <<
"Could not fetch ledger object: " << res.error();
635 std::optional<std::uint32_t>
637 xrpl::uint256
const& key,
638 std::uint32_t
const sequence,
639 boost::asio::yield_context yield
642 LOG(log_.debug()) <<
"Fetching ledger object for seq " << sequence
643 <<
", key = " << xrpl::to_string(key);
644 if (
auto const res = executor_.read(yield, schema_->selectObject, key, sequence); res) {
645 if (
auto const result = res->template get<Blob, std::uint32_t>(); result) {
646 auto [_, seq] = *result;
649 LOG(log_.debug()) <<
"Could not fetch ledger object sequence - no rows";
651 LOG(log_.error()) <<
"Could not fetch ledger object sequence: " << res.error();
657 std::optional<TransactionAndMetadata>
658 fetchTransaction(xrpl::uint256
const& hash, boost::asio::yield_context yield)
const override
660 if (
auto const res = executor_.read(yield, schema_->selectTransaction, hash); res) {
661 if (
auto const maybeValue = res->template get<Blob, Blob, uint32_t, uint32_t>();
663 auto [transaction, meta, seq, date] = *maybeValue;
664 return std::make_optional<TransactionAndMetadata>(transaction, meta, seq, date);
667 LOG(log_.debug()) <<
"Could not fetch transaction - no rows";
669 LOG(log_.error()) <<
"Could not fetch transaction: " << res.error();
675 std::optional<xrpl::uint256>
678 std::uint32_t
const ledgerSequence,
679 boost::asio::yield_context yield
682 if (
auto const res = executor_.read(yield, schema_->selectSuccessor, key, ledgerSequence);
684 if (
auto const result = res->template get<xrpl::uint256>(); result) {
685 if (*result == kLastKey)
690 LOG(log_.debug()) <<
"Could not fetch successor - no rows";
692 LOG(log_.error()) <<
"Could not fetch successor: " << res.error();
698 std::vector<TransactionAndMetadata>
700 std::vector<xrpl::uint256>
const& hashes,
701 boost::asio::yield_context yield
707 auto const numHashes = hashes.size();
708 std::vector<TransactionAndMetadata> results;
709 results.reserve(numHashes);
711 std::vector<Statement> statements;
712 statements.reserve(numHashes);
714 auto const timeDiff =
util::timed([
this, yield, &results, &hashes, &statements]() {
719 std::back_inserter(statements),
720 [
this](
auto const& hash) {
return schema_->selectTransaction.bind(hash); }
723 auto const entries = executor_.readEach(yield, statements);
725 std::cbegin(entries),
727 std::back_inserter(results),
729 if (
auto const maybeRow = res.template get<Blob, Blob, uint32_t, uint32_t>();
738 ASSERT(numHashes == results.size(),
"Number of hashes and results must match");
739 LOG(log_.debug()) <<
"Fetched " << numHashes <<
" transactions from database in "
740 << timeDiff <<
" milliseconds";
746 std::vector<xrpl::uint256>
const& keys,
747 std::uint32_t
const sequence,
748 boost::asio::yield_context yield
754 auto const numKeys = keys.size();
755 LOG(log_.trace()) <<
"Fetching " << numKeys <<
" objects";
757 std::vector<Blob> results;
758 results.reserve(numKeys);
760 std::vector<Statement> statements;
761 statements.reserve(numKeys);
767 std::back_inserter(statements),
768 [
this, &sequence](
auto const& key) {
return schema_->selectObject.bind(key, sequence); }
771 auto const entries = executor_.readEach(yield, statements);
773 std::cbegin(entries),
775 std::back_inserter(results),
776 [](
auto const& res) -> Blob {
777 if (
auto const maybeValue = res.template get<Blob>(); maybeValue)
784 LOG(log_.trace()) <<
"Fetched " << numKeys <<
" objects";
788 std::vector<LedgerObject>
790 std::uint32_t
const ledgerSequence,
791 boost::asio::yield_context yield
794 auto const [keys, timeDiff] =
795 util::timed([
this, &ledgerSequence, yield]() -> std::vector<xrpl::uint256> {
796 auto const res = executor_.read(yield, schema_->selectDiff, ledgerSequence);
798 LOG(log_.error()) <<
"Could not fetch ledger diff: " << res.error()
799 <<
"; ledger = " << ledgerSequence;
803 auto const& results = res.value();
806 <<
"Could not fetch ledger diff - no rows; ledger = " << ledgerSequence;
810 std::vector<xrpl::uint256> resultKeys;
812 resultKeys.push_back(key);
821 LOG(log_.debug()) <<
"Fetched " << keys.size() <<
" diff hashes from database in "
822 << timeDiff <<
" milliseconds";
825 std::vector<LedgerObject> results;
826 results.reserve(keys.size());
832 std::back_inserter(results),
833 [](
auto const& key,
auto const& obj) {
return LedgerObject{key, obj}; }
839 std::optional<std::string>
841 std::string
const& migratorName,
842 boost::asio::yield_context yield
845 auto const res = executor_.read(yield, schema_->selectMigratorStatus,
Text(migratorName));
847 LOG(log_.error()) <<
"Could not fetch migrator status: " << res.error();
851 auto const& results = res.value();
862 std::expected<std::vector<std::pair<boost::uuids::uuid, std::string>>, std::string>
865 auto const readResult = executor_.read(yield, schema_->selectClioNodesData);
867 return std::unexpected{readResult.error().message()};
869 std::vector<std::pair<boost::uuids::uuid, std::string>> result;
872 result.emplace_back(uuid, std::move(message));
881 LOG(log_.trace()) <<
" Writing ledger object " << key.size() <<
":" << seq <<
" ["
882 << blob.size() <<
" bytes]";
885 executor_.write(schema_->insertDiff, seq, key);
887 executor_.write(schema_->insertObject, std::move(key), seq, std::move(blob));
891 writeSuccessor(std::string&& key, std::uint32_t
const seq, std::string&& successor)
override
893 LOG(log_.trace()) <<
"Writing successor. key = " << key.size() <<
" bytes. "
894 <<
" seq = " << std::to_string(seq) <<
" successor = " << successor.size()
896 ASSERT(!key.empty(),
"Key must not be empty");
897 ASSERT(!successor.empty(),
"Successor must not be empty");
899 executor_.write(schema_->insertSuccessor, std::move(key), seq, std::move(successor));
905 std::vector<Statement> statements;
906 statements.reserve(
data.size() * 10);
908 for (
auto& record :
data) {
909 std::ranges::transform(
910 record.accounts, std::back_inserter(statements), [
this, &record](
auto&& account) {
911 return schema_->insertAccountTx.bind(
912 std::forward<decltype(account)>(account),
913 std::make_tuple(record.ledgerSequence, record.transactionIndex),
920 executor_.write(std::move(statements));
926 std::vector<Statement> statements;
927 statements.reserve(record.accounts.size());
929 std::ranges::transform(
930 record.accounts, std::back_inserter(statements), [
this, &record](
auto&& account) {
931 return schema_->insertAccountTx.bind(
932 std::forward<decltype(account)>(account),
933 std::make_tuple(record.ledgerSequence, record.transactionIndex),
939 executor_.write(std::move(statements));
945 std::vector<Statement> statements;
946 statements.reserve(
data.size());
948 std::ranges::transform(
data, std::back_inserter(statements), [
this](
auto const& record) {
949 return schema_->insertNFTTx.bind(
951 std::make_tuple(record.ledgerSequence, record.transactionIndex),
956 executor_.write(std::move(statements));
961 std::vector<MPTokenIssuanceTransactionsData>
const&
data
964 std::vector<Statement> statements;
965 statements.reserve(
data.size());
967 std::ranges::transform(
data, std::back_inserter(statements), [
this](
auto const& record) {
968 return schema_->insertMPTokenIssuanceTx.bind(
969 record.mptIssuanceID,
970 std::make_tuple(record.ledgerSequence, record.transactionIndex),
975 executor_.write(std::move(statements));
980 std::vector<MPTokenIssuanceTransactionsData>
const&
data
983 std::size_t numStatements = 0u;
984 for (
auto const& record :
data)
985 numStatements += record.accounts.size();
987 std::vector<Statement> statements;
988 statements.reserve(numStatements);
990 for (
auto const& record :
data) {
991 std::ranges::transform(
993 std::back_inserter(statements),
994 [
this, &record](
auto const& account) {
995 return schema_->insertAccountMPTokenIssuanceTx.bind(
996 record.mptIssuanceID,
998 std::make_tuple(record.ledgerSequence, record.transactionIndex),
1005 executor_.write(std::move(statements));
1011 std::uint32_t
const seq,
1012 std::uint32_t
const date,
1013 std::string&& transaction,
1014 std::string&& metadata
1017 LOG(log_.trace()) <<
"Writing txn to database";
1019 executor_.write(schema_->insertLedgerTransaction, seq, hash);
1021 schema_->insertTransaction,
1025 std::move(transaction),
1033 std::vector<Statement> statements;
1034 statements.reserve(
data.size() * 3);
1037 if (!record.onlyUriChanged) {
1038 statements.push_back(schema_->insertNFT.bind(
1039 record.tokenID, record.ledgerSequence, record.owner, record.isBurned
1048 statements.push_back(schema_->insertIssuerNFT.bind(
1049 xrpl::nft::getIssuer(record.tokenID),
1050 static_cast<uint32_t
>(xrpl::nft::getTaxon(record.tokenID)),
1053 statements.push_back(schema_->insertNFTURI.bind(
1054 record.tokenID, record.ledgerSequence, *record.uri
1059 statements.push_back(
1061 schema_->insertNFTURI.bind(record.tokenID, record.ledgerSequence, *record.uri)
1066 executor_.writeEach(std::move(statements));
1072 std::vector<Statement> statements;
1073 statements.reserve(
data.size());
1074 for (
auto [mptId, holder] :
data)
1075 statements.push_back(schema_->insertMPTHolder.bind(mptId, holder));
1077 executor_.write(std::move(statements));
1090 executor_.writeSync(
1091 schema_->insertMigratorStatus,
1100 executor_.writeSync(
1108 return executor_.isTooBusy();
1114 return executor_.stats();
1127 auto const res = executor_.writeSync(statement);
1128 auto maybeSuccess = res->template get<bool>();
1129 if (not maybeSuccess) {
1130 LOG(log_.error()) <<
"executeSyncUpdate - error getting result - no row";
1134 if (not *maybeSuccess) {
1135 LOG(log_.warn()) <<
"Update failed. Checking if DB state is what we expect";
1142 return rng && rng->maxSequence == ledgerSequence_;
1166 Statement
const& statement,
1167 std::size_t
const cursorIdx,
1168 std::size_t
const limitIdx,
1169 std::uint32_t
const limit,
1171 std::optional<TransactionsCursor>
const& cursorIn,
1172 boost::asio::yield_context yield
1177 return {.txns = {}, .cursor = {}};
1179 auto cursor = cursorIn;
1180 if (cursor.has_value()) {
1181 statement.
bindAt(cursorIdx, cursor->asTuple());
1185 auto const ledgerSequence = forward ? rng->minSequence : rng->maxSequence;
1186 auto const transactionIndex = forward ? 0u : std::numeric_limits<std::uint32_t>::max();
1187 statement.
bindAt(cursorIdx, std::make_tuple(ledgerSequence, transactionIndex));
1192 auto const res = executor_.read(yield, statement);
1193 auto const& results = res.value();
1194 if (not results.hasRows()) {
1195 LOG(log_.debug()) <<
"No rows returned";
1199 std::vector<xrpl::uint256> hashes = {};
1200 auto numRows = results.numRows();
1202 for (
auto const& [hash,
data] :
1203 extract<xrpl::uint256, std::tuple<uint32_t, uint32_t>>(results)) {
1204 hashes.push_back(hash);
1206 if (--numRows == 0) {
1207 LOG(log_.debug()) <<
"Setting cursor";
1213 ++cursor->transactionIndex;
1218 LOG(log_.debug()) <<
"MPTokenIssuance Txns = " << txns.size();
1220 if (txns.size() == limit) {
1221 LOG(log_.debug()) <<
"Returning cursor";
1222 return {std::move(txns), cursor};
1225 return {std::move(txns), {}};
BackendInterface(LedgerCacheInterface &cache)
Construct a new backend interface instance.
Definition BackendInterface.hpp:225
std::optional< LedgerRange > hardFetchLedgerRangeNoThrow() const
Fetches the ledger range from DB retrying until no DatabaseError is thrown.
Definition BackendInterface.cpp:53
std::optional< LedgerRange > fetchLedgerRange() const
Fetch the current ledger range.
Definition BackendInterface.cpp:251
std::vector< Blob > fetchLedgerObjects(std::vector< xrpl::uint256 > const &keys, std::uint32_t sequence, boost::asio::yield_context yield) const
Fetches all ledger objects by their keys.
Definition BackendInterface.cpp:95
LedgerCacheInterface const & cache() const
Definition BackendInterface.hpp:256
A simple cache holding one xrpl::LedgerHeader to reduce DB lookups.
Definition LedgerHeaderCache.hpp:22
Cache for an entire ledger.
Definition LedgerCacheInterface.hpp:21
void writeMigratorStatus(std::string const &migratorName, std::string const &status) override
Mark the migration status of a migrator as Migrated in the database.
Definition CassandraBackendFamily.hpp:1088
std::optional< LedgerRange > hardFetchLedgerRange(boost::asio::yield_context yield) const override
Fetches the ledger range from DB.
Definition CassandraBackendFamily.hpp:318
std::optional< xrpl::LedgerHeader > fetchLedgerBySequence(std::uint32_t const sequence, boost::asio::yield_context yield) const override
Fetches a specific ledger by sequence number.
Definition CassandraBackendFamily.hpp:267
TransactionsAndCursor fetchAccountMPTokenIssuanceTransactions(xrpl::uint192 const &mptIssuanceID, xrpl::AccountID const &account, std::uint32_t const limit, bool const forward, std::optional< TransactionsCursor > const &cursorIn, boost::asio::yield_context yield) const override
Fetches transactions for a particular MPTokenIssuance ID involving a particular account.
Definition CassandraBackendFamily.hpp:541
std::optional< NFT > fetchNFT(xrpl::uint256 const &tokenID, std::uint32_t const ledgerSequence, boost::asio::yield_context yield) const override
Fetches a specific NFT.
Definition CassandraBackendFamily.hpp:403
void startWrites() const override
Starts a write transaction with the DB. No-op for cassandra.
Definition CassandraBackendFamily.hpp:1081
void doWriteLedgerObject(std::string &&key, std::uint32_t const seq, std::string &&blob) override
Writes a ledger object to the database.
Definition CassandraBackendFamily.hpp:879
std::chrono::milliseconds initialRetryDelay() const override
Definition CassandraBackendFamily.hpp:146
std::optional< std::uint32_t > doFetchLedgerObjectSeq(xrpl::uint256 const &key, std::uint32_t const sequence, boost::asio::yield_context yield) const override
The database-specific implementation for fetching a ledger object sequence.
Definition CassandraBackendFamily.hpp:636
std::optional< xrpl::LedgerHeader > fetchLedgerByHash(xrpl::uint256 const &hash, boost::asio::yield_context yield) const override
Fetches a specific ledger by hash.
Definition CassandraBackendFamily.hpp:298
TransactionsAndCursor fetchAccountTransactions(xrpl::AccountID const &account, std::uint32_t const limit, bool forward, std::optional< TransactionsCursor > const &txnCursor, boost::asio::yield_context yield) const override
Fetches all transactions for a specific account.
Definition CassandraBackendFamily.hpp:161
std::optional< Blob > doFetchLedgerObject(xrpl::uint256 const &key, std::uint32_t const sequence, boost::asio::yield_context yield) const override
The database-specific implementation for fetching a ledger object.
Definition CassandraBackendFamily.hpp:613
void writeNFTs(std::vector< NFTsData > const &data) override
Writes NFTs to the database.
Definition CassandraBackendFamily.hpp:1031
void writeNFTTransactions(std::vector< NFTTransactionsData > const &data) override
Write NFTs transactions.
Definition CassandraBackendFamily.hpp:943
void writeNodeMessage(boost::uuids::uuid const &uuid, std::string message) override
Write a node message. Used by ClusterCommunicationService.
Definition CassandraBackendFamily.hpp:1098
std::optional< std::uint32_t > fetchLatestLedgerSequence(boost::asio::yield_context yield) const override
Fetches the latest ledger sequence.
Definition CassandraBackendFamily.hpp:247
CassandraBackendFamily(SettingsProviderType settingsProvider, data::LedgerCacheInterface &cache, bool readOnly)
Create a new cassandra/scylla backend instance.
Definition CassandraBackendFamily.hpp:95
void writeAccountMPTokenIssuanceTransactions(std::vector< MPTokenIssuanceTransactionsData > const &data) override
Write MPTokenIssuance transaction index rows to the account_mptoken_issuance_transactions table.
Definition CassandraBackendFamily.hpp:979
MPTHoldersAndCursor fetchMPTHolders(xrpl::uint192 const &mptID, std::uint32_t const limit, std::optional< xrpl::AccountID > const &cursorIn, std::uint32_t const ledgerSequence, boost::asio::yield_context yield) const override
Fetches all holders' balances for a MPTIssuanceID.
Definition CassandraBackendFamily.hpp:568
std::optional< xrpl::uint256 > doFetchSuccessorKey(xrpl::uint256 key, std::uint32_t const ledgerSequence, boost::asio::yield_context yield) const override
Database-specific implementation of fetching the successor key.
Definition CassandraBackendFamily.hpp:676
TransactionsAndCursor fetchNFTTransactions(xrpl::uint256 const &tokenID, std::uint32_t const limit, bool const forward, std::optional< TransactionsCursor > const &cursorIn, boost::asio::yield_context yield) const override
Fetches all transactions for a specific NFT.
Definition CassandraBackendFamily.hpp:442
bool isTooBusy() const override
Definition CassandraBackendFamily.hpp:1106
void writeMPTHolders(std::vector< MPTHolderData > const &data) override
Write accounts that started holding onto a MPT.
Definition CassandraBackendFamily.hpp:1070
void writeAccountTransaction(AccountTransactionsData record) override
Write a new account transaction.
Definition CassandraBackendFamily.hpp:924
void writeSuccessor(std::string &&key, std::uint32_t const seq, std::string &&successor) override
Write a new successor.
Definition CassandraBackendFamily.hpp:891
void waitForWritesToFinish() override
Wait for all pending writes to finish.
Definition CassandraBackendFamily.hpp:231
TransactionsAndCursor fetchMPTokenIssuanceTransactions(xrpl::uint192 const &mptIssuanceID, std::uint32_t const limit, bool const forward, std::optional< TransactionsCursor > const &cursorIn, boost::asio::yield_context yield) const override
Fetches transactions for a particular MPTokenIssuance ID.
Definition CassandraBackendFamily.hpp:515
void writeLedger(xrpl::LedgerHeader const &ledgerHeader, std::string &&blob) override
Writes to a specific ledger.
Definition CassandraBackendFamily.hpp:237
std::optional< TransactionAndMetadata > fetchTransaction(xrpl::uint256 const &hash, boost::asio::yield_context yield) const override
Fetches a specific transaction.
Definition CassandraBackendFamily.hpp:658
TransactionsAndCursor fetchMPTokenIssuanceTransactionsImpl(Statement const &statement, std::size_t const cursorIdx, std::size_t const limitIdx, std::uint32_t const limit, bool const forward, std::optional< TransactionsCursor > const &cursorIn, boost::asio::yield_context yield) const
Shared implementation of the two MPTokenIssuance transaction-index fetchers.
Definition CassandraBackendFamily.hpp:1165
boost::json::object stats() const override
Definition CassandraBackendFamily.hpp:1112
std::optional< std::string > fetchMigratorStatus(std::string const &migratorName, boost::asio::yield_context yield) const override
Fetches the status of migrator by name.
Definition CassandraBackendFamily.hpp:840
std::chrono::milliseconds maxRetryDelay() const override
Definition CassandraBackendFamily.hpp:155
std::expected< std::vector< std::pair< boost::uuids::uuid, std::string > >, std::string > fetchClioNodesData(boost::asio::yield_context yield) const override
Fetches the data of all nodes in the cluster.
Definition CassandraBackendFamily.hpp:863
void writeAccountTransactions(std::vector< AccountTransactionsData > data) override
Write a new set of account transactions.
Definition CassandraBackendFamily.hpp:903
std::vector< Blob > doFetchLedgerObjects(std::vector< xrpl::uint256 > const &keys, std::uint32_t const sequence, boost::asio::yield_context yield) const override
The database-specific implementation for fetching ledger objects.
Definition CassandraBackendFamily.hpp:745
CassandraBackendFamily(CassandraBackendFamily &&)=delete
Move constructor is deleted because handle_ is shared by reference with executor.
bool executeSyncUpdate(Statement statement)
Executes statements and tries to write to DB.
Definition CassandraBackendFamily.hpp:1125
void writeMPTokenIssuanceTransactions(std::vector< MPTokenIssuanceTransactionsData > const &data) override
Write MPTokenIssuance transaction index rows to the mptoken_issuance_transactions table.
Definition CassandraBackendFamily.hpp:960
void writeTransaction(std::string &&hash, std::uint32_t const seq, std::uint32_t const date, std::string &&transaction, std::string &&metadata) override
Writes a new transaction.
Definition CassandraBackendFamily.hpp:1009
std::vector< xrpl::uint256 > fetchAllTransactionHashesInLedger(std::uint32_t const ledgerSequence, boost::asio::yield_context yield) const override
Fetches all transaction hashes from a specific ledger.
Definition CassandraBackendFamily.hpp:366
std::vector< TransactionAndMetadata > fetchTransactions(std::vector< xrpl::uint256 > const &hashes, boost::asio::yield_context yield) const override
Fetches multiple transactions.
Definition CassandraBackendFamily.hpp:699
std::vector< LedgerObject > fetchLedgerDiff(std::uint32_t const ledgerSequence, boost::asio::yield_context yield) const override
Returns the difference between ledgers.
Definition CassandraBackendFamily.hpp:789
std::vector< TransactionAndMetadata > fetchAllTransactionsInLedger(std::uint32_t const ledgerSequence, boost::asio::yield_context yield) const override
Fetches all transactions from a specific ledger.
Definition CassandraBackendFamily.hpp:356
Represents a handle to the cassandra database cluster.
Definition Handle.hpp:27
void bindAt(std::size_t const idx, Type &&value) const
Binds an argument to a specific index.
Definition Statement.hpp:76
A simple thread-safe logger for the channel specified in the constructor.
Definition Logger.hpp:78
The requirements of an execution strategy.
Definition Concepts.hpp:35
The requirements of a settings provider.
Definition Concepts.hpp:24
This namespace implements a wrapper for the Cassandra C++ driver.
Definition CassandraBackendFamily.hpp:47
impl::ResultExtractor< Types... > extract(Handle::ResultType const &result)
Extracts the results into series of std::tuple<Types...> by creating a simple wrapper with an STL inp...
Definition Handle.hpp:314
This namespace implements the data access layer and related components.
Definition AmendmentCenter.cpp:56
xrpl::LedgerHeader deserializeHeader(xrpl::Slice data)
Deserializes a xrpl::LedgerHeader from xrpl::Slice of data.
Definition LedgerUtils.hpp:240
auto timed(FnType &&func)
Profiler function to measure the time a function execution consumes.
Definition Profiler.hpp:21
Struct used to keep track of what to write to account_transactions/account_tx tables.
Definition DBHelpers.hpp:26
Represents an NFT state at a particular ledger.
Definition DBHelpers.hpp:93
Struct to store ledger header cache entry and the sequence it belongs to.
Definition LedgerHeaderCache.hpp:29
Represents an object in the ledger.
Definition Types.hpp:22
Stores a range of sequences as a min and max pair.
Definition Types.hpp:243
Represents an array of MPTokens.
Definition Types.hpp:235
Represests a bundle of transactions with metadata and a cursor to the next page.
Definition Types.hpp:153
A strong type wrapper for int32_t.
Definition Types.hpp:38
A strong type wrapper for string.
Definition Types.hpp:49