20 #include <ripple/app/reporting/DBHelpers.h>
21 #include <ripple/app/reporting/ReportingETL.h>
23 #include <ripple/beast/core/CurrentThreadName.h>
24 #include <ripple/json/json_reader.h>
25 #include <ripple/json/json_writer.h>
26 #include <boost/asio/connect.hpp>
27 #include <boost/asio/ip/tcp.hpp>
28 #include <boost/beast/core.hpp>
29 #include <boost/beast/websocket.hpp>
43 ss <<
"LedgerInfo { Sequence : " << info.
seq
58 while (!
stopping_ && (sle = writeQueue.pop()))
61 if (!ledger->exists(sle->key()))
62 ledger->rawInsert(sle);
76 org::xrpl::rpc::v1::GetLedgerResponse& data)
79 for (
auto& txn : data.transactions_list().transactions())
81 auto& raw = txn.transaction_blob();
86 auto txSerializer = std::make_shared<Serializer>(sttx.getSerializer());
89 sttx.getTransactionID(), ledger->info().seq, txn.metadata_blob()};
92 std::make_shared<Serializer>(txMeta.getAsObject().getSerializer());
96 <<
"Inserting transaction = " << sttx.getTransactionID();
97 uint256 nodestoreHash = ledger->rawTxInsertWithHash(
98 sttx.getTransactionID(), txSerializer, metaSerializer);
101 return accountTxData;
108 auto ledger = std::const_pointer_cast<Ledger>(
113 <<
"Database is not empty";
130 <<
"Deserialized ledger header. "
135 ledger->stateMap().clearSynching();
136 ledger->txMap().clearSynching();
138 #ifdef RIPPLED_REPORTING
146 std::thread asyncWriter{[
this, &ledger, &writeQueue]() {
158 writeQueue.
push(
null);
167 #ifdef RIPPLED_REPORTING
169 ledger->info(), accountTxData,
app_.getPgPool(),
journal_);
174 JLOG(
journal_.
debug()) <<
"Time to download and store ledger = "
175 << ((end - start).count()) / 1000000000.0;
183 <<
"Flushing ledger. "
186 auto& accountHash = ledger->info().accountHash;
187 auto& txHash = ledger->info().txHash;
188 auto& ledgerHash = ledger->info().hash;
200 addRaw(ledger->info(), s);
213 <<
"Flushed " << numFlushed
214 <<
" nodes to nodestore from stateMap";
216 <<
"Flushed " << numTxFlushed
217 <<
" nodes to nodestore from txMap";
221 << (end - start).count() / 1000000000.0
227 <<
"Flushed 0 nodes from state map";
230 if (numTxFlushed == 0)
233 <<
"Flushed 0 nodes from tx map";
237 if (ledger->stateMap().getHash().as_uint256() != accountHash)
241 <<
"State map hash does not match. "
242 <<
"Expected hash = " <<
strHex(accountHash) <<
"Actual hash = "
243 <<
strHex(ledger->stateMap().getHash().as_uint256());
244 Throw<std::runtime_error>(
"state map hash mismatch");
247 if (ledger->txMap().getHash().as_uint256() != txHash)
251 <<
"Tx map hash does not match. "
252 <<
"Expected hash = " <<
strHex(txHash) <<
"Actual hash = "
253 <<
strHex(ledger->txMap().getHash().as_uint256());
254 Throw<std::runtime_error>(
"tx map hash mismatch");
257 if (ledger->info().hash != ledgerHash)
261 <<
"Ledger hash does not match. "
262 <<
"Expected hash = " <<
strHex(ledgerHash)
263 <<
"Actual hash = " <<
strHex(ledger->info().hash);
264 Throw<std::runtime_error>(
"ledger hash mismatch");
268 <<
"Successfully flushed ledger! "
284 <<
"Attempting to publish ledger = "
286 size_t numAttempts = 0;
295 <<
"Trying to publish. Could not find ledger with sequence = "
306 if (numAttempts >= maxAttempts)
309 <<
"Failed to publish ledger after "
310 << numAttempts <<
" attempts.";
314 <<
"Attempting to become ETL writer";
321 <<
"In strict read-only mode. "
322 <<
"Skipping publishing this ledger. "
323 <<
"Beginning fast forward.";
351 <<
"Attempting to fetch ledger with sequence = "
357 <<
"GetLedger reply = " << response->DebugString();
365 <<
"Attempting to fetch ledger with sequence = "
371 <<
"GetLedger reply = " << response->DebugString();
378 org::xrpl::rpc::v1::GetLedgerResponse& rawData)
381 <<
"Beginning ledger update";
387 <<
"Deserialized ledger header. "
390 next->setLedgerInfo(lgrInfo);
392 next->stateMap().clearSynching();
393 next->txMap().clearSynching();
400 <<
"Inserted all transactions. Number of transactions = "
401 << rawData.transactions_list().transactions_size();
403 for (
auto& obj : rawData.ledger_objects().objects())
406 auto& data = obj.data();
409 if (data.size() == 0)
412 <<
"Erasing object = " << key;
413 if (next->exists(key))
421 if (next->exists(key))
424 <<
"Replacing object = " << key;
425 next->rawReplace(sle);
430 <<
"Inserting object = " << key;
431 next->rawInsert(sle);
437 <<
"Inserted/modified/deleted all objects. Number of objects = "
438 << rawData.ledger_objects().objects_size();
440 if (!rawData.skiplist_included())
442 next->updateSkipList();
445 <<
"tx process is not sending skiplist. This indicates that the tx "
446 "process is parsing metadata instead of doing a SHAMap diff. "
447 "Make sure tx process is running the same code as reporting to "
448 "use SHAMap diff instead of parsing metadata";
452 <<
"Finished ledger update. "
454 return {std::move(next), std::move(accountTxData)};
483 <<
"Starting etl pipeline";
491 Throw<std::runtime_error>(
"runETLPipeline: parent ledger is null");
496 constexpr uint32_t maxQueueSize = 1000;
499 transformQueue{maxQueueSize};
506 uint32_t currentSequence = startSequence;
523 auto time = ((end - start).count()) / 1000000000.0;
525 fetchResponse->transactions_list().transactions_size() / time;
528 <<
" . Extract phase tps = " << tps;
541 transformQueue.push(std::move(fetchResponse));
545 transformQueue.push({});
551 loadQueue{maxQueueSize};
561 while (!writeConflict)
564 transformQueue.pop()};
575 auto [next, accountTxData] =
579 auto duration = ((end - start).count()) / 1000000000.0;
593 &lastPublishedSequence,
597 size_t totalTransactions = 0;
598 double totalTime = 0;
599 while (!writeConflict)
604 result{loadQueue.pop()};
612 auto& ledger = result->first;
613 auto& accountTxData = result->second;
623 #ifdef RIPPLED_REPORTING
624 if (!writeToPostgres(
625 ledger->info(), accountTxData,
app_.getPgPool(),
journal_))
626 writeConflict =
true;
634 lastPublishedSequence = ledger->info().seq;
637 auto kvTime = ((mid - start).count()) / 1000000000.0;
638 auto relationalTime = ((end - mid).count()) / 1000000000.0;
640 size_t numTxns = accountTxData.size();
642 totalTransactions += numTxns;
644 <<
"Load phase of etl : "
645 <<
"Successfully published ledger! Ledger info: "
647 <<
". txn count = " << numTxns
648 <<
". key-value write time = " << kvTime
649 <<
". relational write time = " << relationalTime
650 <<
". key-value tps = " << numTxns / kvTime
651 <<
". relational tps = " << numTxns / relationalTime
652 <<
". total key-value tps = " << totalTransactions / totalTime;
663 <<
"Stopping etl pipeline";
665 return lastPublishedSequence;
681 auto ledger = std::const_pointer_cast<Ledger>(
686 <<
"Database is empty. Will download a ledger "
692 <<
"ledger sequence specified in config. "
693 <<
"Will begin ETL process starting with ledger "
701 <<
"Waiting for next ledger to be validated by network...";
704 if (mostRecentValidated)
707 <<
"Ledger " << *mostRecentValidated
708 <<
" has been validated. "
715 <<
"The wait for the next validated "
716 <<
"ledger has been aborted. "
717 <<
"Exiting monitor loop";
726 Throw<std::runtime_error>(
727 "start sequence specified but db is already populated");
731 <<
"Database already populated. Picking up from the tip of history";
737 <<
"Failed to load initial ledger. Exiting monitor loop";
744 uint32_t nextSequence = ledger->info().seq + 1;
747 <<
"Database is populated. "
748 <<
"Starting monitor loop. sequence = "
754 <<
"Ledger with sequence = " << nextSequence
755 <<
" has been validated by the network. "
756 <<
"Attempting to find in database and publish";
773 constexpr
size_t timeoutSeconds = 10;
779 <<
"Failed to publish ledger with sequence = " << nextSequence
780 <<
" . Beginning ETL";
786 <<
"Aborting ETL. Falling back to publishing";
789 nextSequence = *lastPublished + 1;
801 JLOG(
journal_.
debug()) <<
"Starting reporting in strict read only mode";
806 uint32_t sequence = *mostRecent;
831 , journal_(app.journal(
"ReportingETL"))
832 , publishStrand_(app_.getIOService())
833 , loadBalancer_(*this)
842 auto& vals = section.
values();
853 source.
find(
"source_ws_port");
858 source.
find(
"source_grpc_port");
904 section.
find(
"flush_interval");