20 #include <ripple/app/rdb/backend/RelationalDBInterfacePostgres.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
175 JLOG(
journal_.
debug()) <<
"Time to download and store ledger = "
176 << ((end -
start).count()) / 1000000000.0;
184 <<
"Flushing ledger. "
187 auto& accountHash = ledger->info().accountHash;
188 auto& txHash = ledger->info().txHash;
189 auto& ledgerHash = ledger->info().hash;
201 addRaw(ledger->info(), s);
214 <<
"Flushed " << numFlushed
215 <<
" nodes to nodestore from stateMap";
217 <<
"Flushed " << numTxFlushed
218 <<
" nodes to nodestore from txMap";
222 << (end -
start).count() / 1000000000.0
228 <<
"Flushed 0 nodes from state map";
231 if (numTxFlushed == 0)
234 <<
"Flushed 0 nodes from tx map";
238 if (ledger->stateMap().getHash().as_uint256() != accountHash)
242 <<
"State map hash does not match. "
243 <<
"Expected hash = " <<
strHex(accountHash) <<
"Actual hash = "
244 <<
strHex(ledger->stateMap().getHash().as_uint256());
245 Throw<std::runtime_error>(
"state map hash mismatch");
248 if (ledger->txMap().getHash().as_uint256() != txHash)
252 <<
"Tx map hash does not match. "
253 <<
"Expected hash = " <<
strHex(txHash) <<
"Actual hash = "
254 <<
strHex(ledger->txMap().getHash().as_uint256());
255 Throw<std::runtime_error>(
"tx map hash mismatch");
258 if (ledger->info().hash != ledgerHash)
262 <<
"Ledger hash does not match. "
263 <<
"Expected hash = " <<
strHex(ledgerHash)
264 <<
"Actual hash = " <<
strHex(ledger->info().hash);
265 Throw<std::runtime_error>(
"ledger hash mismatch");
269 <<
"Successfully flushed ledger! "
285 <<
"Attempting to publish ledger = "
287 size_t numAttempts = 0;
296 <<
"Trying to publish. Could not find ledger with sequence = "
307 if (numAttempts >= maxAttempts)
310 <<
"Failed to publish ledger after "
311 << numAttempts <<
" attempts.";
315 <<
"Attempting to become ETL writer";
322 <<
"In strict read-only mode. "
323 <<
"Skipping publishing this ledger. "
324 <<
"Beginning fast forward.";
352 <<
"Attempting to fetch ledger with sequence = "
358 <<
"GetLedger reply = " << response->DebugString();
366 <<
"Attempting to fetch ledger with sequence = "
372 <<
"GetLedger reply = " << response->DebugString();
379 org::xrpl::rpc::v1::GetLedgerResponse& rawData)
382 <<
"Beginning ledger update";
388 <<
"Deserialized ledger header. "
391 next->setLedgerInfo(lgrInfo);
393 next->stateMap().clearSynching();
394 next->txMap().clearSynching();
401 <<
"Inserted all transactions. Number of transactions = "
402 << rawData.transactions_list().transactions_size();
404 for (
auto& obj : rawData.ledger_objects().objects())
407 auto& data = obj.data();
410 if (data.size() == 0)
413 <<
"Erasing object = " << key;
414 if (next->exists(key))
422 if (next->exists(key))
425 <<
"Replacing object = " << key;
426 next->rawReplace(sle);
431 <<
"Inserting object = " << key;
432 next->rawInsert(sle);
438 <<
"Inserted/modified/deleted all objects. Number of objects = "
439 << rawData.ledger_objects().objects_size();
441 if (!rawData.skiplist_included())
443 next->updateSkipList();
446 <<
"tx process is not sending skiplist. This indicates that the tx "
447 "process is parsing metadata instead of doing a SHAMap diff. "
448 "Make sure tx process is running the same code as reporting to "
449 "use SHAMap diff instead of parsing metadata";
453 <<
"Finished ledger update. "
455 return {std::move(next), std::move(accountTxData)};
484 <<
"Starting etl pipeline";
492 Throw<std::runtime_error>(
"runETLPipeline: parent ledger is null");
497 constexpr uint32_t maxQueueSize = 1000;
500 transformQueue{maxQueueSize};
507 uint32_t currentSequence = startSequence;
524 auto time = ((end -
start).count()) / 1000000000.0;
526 fetchResponse->transactions_list().transactions_size() / time;
529 <<
" . Extract phase tps = " << tps;
542 transformQueue.push(std::move(fetchResponse));
546 transformQueue.push({});
552 loadQueue{maxQueueSize};
562 while (!writeConflict)
565 transformQueue.pop()};
576 auto [next, accountTxData] =
580 auto duration = ((end -
start).count()) / 1000000000.0;
594 [
this, &lastPublishedSequence, &loadQueue, &writeConflict]() {
596 size_t totalTransactions = 0;
597 double totalTime = 0;
598 while (!writeConflict)
603 result{loadQueue.pop()};
611 auto& ledger = result->first;
612 auto& accountTxData = result->second;
622 #ifdef RIPPLED_REPORTING
626 ledger->info(), accountTxData))
627 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 = "
653 << totalTransactions / totalTime;
664 <<
"Stopping etl pipeline";
666 return lastPublishedSequence;
682 auto ledger = std::const_pointer_cast<Ledger>(
687 <<
"Database is empty. Will download a ledger "
693 <<
"ledger sequence specified in config. "
694 <<
"Will begin ETL process starting with ledger "
702 <<
"Waiting for next ledger to be validated by network...";
705 if (mostRecentValidated)
708 <<
"Ledger " << *mostRecentValidated
709 <<
" has been validated. "
716 <<
"The wait for the next validated "
717 <<
"ledger has been aborted. "
718 <<
"Exiting monitor loop";
727 Throw<std::runtime_error>(
728 "start sequence specified but db is already populated");
732 <<
"Database already populated. Picking up from the tip of history";
738 <<
"Failed to load initial ledger. Exiting monitor loop";
745 uint32_t nextSequence = ledger->info().seq + 1;
748 <<
"Database is populated. "
749 <<
"Starting monitor loop. sequence = "
755 <<
"Ledger with sequence = " << nextSequence
756 <<
" has been validated by the network. "
757 <<
"Attempting to find in database and publish";
774 constexpr
size_t timeoutSeconds = 10;
780 <<
"Failed to publish ledger with sequence = " << nextSequence
781 <<
" . Beginning ETL";
787 <<
"Aborting ETL. Falling back to publishing";
790 nextSequence = *lastPublished + 1;
802 JLOG(
journal_.
debug()) <<
"Starting reporting in strict read only mode";
807 uint32_t sequence = *mostRecent;
831 , journal_(app.journal(
"ReportingETL"))
832 , publishStrand_(app_.getIOService())
833 , loadBalancer_(*this)
838 #ifndef RIPPLED_REPORTING
839 Throw<std::runtime_error>(
840 "Config file specifies reporting, but software was not built with "
841 "-Dreporting=1. To use reporting, configure CMake with "
845 Throw<std::runtime_error>(
846 "Reporting requires tx tables. Set use_tx_tables=1 in config "
847 "file, under [ledger_tx_tables] section");
852 auto& vals = section.
values();
863 source.
find(
"source_ws_port");
868 source.
find(
"source_grpc_port");
914 section.
find(
"flush_interval");