mirror of
https://github.com/XRPLF/clio.git
synced 2025-12-06 17:27:58 +00:00
@@ -9,7 +9,7 @@ formatter="clang-format -i"
|
|||||||
first=$(git diff $sources)
|
first=$(git diff $sources)
|
||||||
find $sources -type f \( -name '*.cpp' -o -name '*.h' -o -name '*.ipp' \) -print0 | xargs -0 $formatter
|
find $sources -type f \( -name '*.cpp' -o -name '*.h' -o -name '*.ipp' \) -print0 | xargs -0 $formatter
|
||||||
second=$(git diff $sources)
|
second=$(git diff $sources)
|
||||||
changes=$(diff <(echo "$first") <(echo "$second") | wc -l)
|
changes=$(diff <(echo "$first") <(echo "$second") | wc -l | sed -e 's/^[[:space:]]*//')
|
||||||
|
|
||||||
if [ "$changes" != "0" ]; then
|
if [ "$changes" != "0" ]; then
|
||||||
cat <<\EOF
|
cat <<\EOF
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ if(BUILD_TESTS)
|
|||||||
unittests/Backend.cpp
|
unittests/Backend.cpp
|
||||||
unittests/Logger.cpp
|
unittests/Logger.cpp
|
||||||
unittests/Config.cpp
|
unittests/Config.cpp
|
||||||
|
unittests/ProfilerTest.cpp
|
||||||
unittests/DOSGuard.cpp)
|
unittests/DOSGuard.cpp)
|
||||||
include(CMake/deps/gtest.cmake)
|
include(CMake/deps/gtest.cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <backend/CassandraBackend.h>
|
#include <backend/CassandraBackend.h>
|
||||||
#include <backend/DBHelpers.h>
|
#include <backend/DBHelpers.h>
|
||||||
#include <log/Logger.h>
|
#include <log/Logger.h>
|
||||||
|
#include <util/Profiler.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -524,32 +525,30 @@ CassandraBackend::fetchTransactions(
|
|||||||
std::vector<TransactionAndMetadata> results{numHashes};
|
std::vector<TransactionAndMetadata> results{numHashes};
|
||||||
std::vector<std::shared_ptr<ReadCallbackData<result_type>>> cbs;
|
std::vector<std::shared_ptr<ReadCallbackData<result_type>>> cbs;
|
||||||
cbs.reserve(numHashes);
|
cbs.reserve(numHashes);
|
||||||
auto start = std::chrono::system_clock::now();
|
auto timeDiff = util::timed([&]() {
|
||||||
|
for (std::size_t i = 0; i < hashes.size(); ++i)
|
||||||
|
{
|
||||||
|
CassandraStatement statement{selectTransaction_};
|
||||||
|
statement.bindNextBytes(hashes[i]);
|
||||||
|
|
||||||
for (std::size_t i = 0; i < hashes.size(); ++i)
|
cbs.push_back(std::make_shared<ReadCallbackData<result_type>>(
|
||||||
{
|
numOutstanding, handler, [i, &results](auto& result) {
|
||||||
CassandraStatement statement{selectTransaction_};
|
if (result.hasResult())
|
||||||
statement.bindNextBytes(hashes[i]);
|
results[i] = {
|
||||||
|
result.getBytes(),
|
||||||
|
result.getBytes(),
|
||||||
|
result.getUInt32(),
|
||||||
|
result.getUInt32()};
|
||||||
|
}));
|
||||||
|
|
||||||
cbs.push_back(std::make_shared<ReadCallbackData<result_type>>(
|
executeAsyncRead(statement, processAsyncRead, *cbs[i]);
|
||||||
numOutstanding, handler, [i, &results](auto& result) {
|
}
|
||||||
if (result.hasResult())
|
assert(results.size() == cbs.size());
|
||||||
results[i] = {
|
|
||||||
result.getBytes(),
|
|
||||||
result.getBytes(),
|
|
||||||
result.getUInt32(),
|
|
||||||
result.getUInt32()};
|
|
||||||
}));
|
|
||||||
|
|
||||||
executeAsyncRead(statement, processAsyncRead, *cbs[i]);
|
// suspend the coroutine until completion handler is called.
|
||||||
}
|
result.get();
|
||||||
assert(results.size() == cbs.size());
|
numReadRequestsOutstanding_ -= hashes.size();
|
||||||
|
});
|
||||||
// suspend the coroutine until completion handler is called.
|
|
||||||
result.get();
|
|
||||||
numReadRequestsOutstanding_ -= hashes.size();
|
|
||||||
|
|
||||||
auto end = std::chrono::system_clock::now();
|
|
||||||
for (auto const& cb : cbs)
|
for (auto const& cb : cbs)
|
||||||
{
|
{
|
||||||
if (cb->errored)
|
if (cb->errored)
|
||||||
@@ -557,10 +556,7 @@ CassandraBackend::fetchTransactions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
log_.debug() << "Fetched " << numHashes
|
log_.debug() << "Fetched " << numHashes
|
||||||
<< " transactions from Cassandra in "
|
<< " transactions from Cassandra in " << timeDiff
|
||||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
||||||
end - start)
|
|
||||||
.count()
|
|
||||||
<< " milliseconds";
|
<< " milliseconds";
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include <etl/ReportingETL.h>
|
#include <etl/ReportingETL.h>
|
||||||
#include <log/Logger.h>
|
#include <log/Logger.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <util/Profiler.h>
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
@@ -807,66 +808,68 @@ ETLSourceImpl<Derived>::loadInitialLedger(
|
|||||||
backend_->cache().setFull();
|
backend_->cache().setFull();
|
||||||
if (!cacheOnly)
|
if (!cacheOnly)
|
||||||
{
|
{
|
||||||
auto start = std::chrono::system_clock::now();
|
auto seconds = util::timed<std::chrono::seconds>([&]() {
|
||||||
for (auto& key : edgeKeys)
|
for (auto& key : edgeKeys)
|
||||||
{
|
|
||||||
log_.debug() << "Writing edge key = " << ripple::strHex(key);
|
|
||||||
auto succ = backend_->cache().getSuccessor(
|
|
||||||
*ripple::uint256::fromVoidChecked(key), sequence);
|
|
||||||
if (succ)
|
|
||||||
backend_->writeSuccessor(
|
|
||||||
std::move(key), sequence, uint256ToString(succ->key));
|
|
||||||
}
|
|
||||||
ripple::uint256 prev = Backend::firstKey;
|
|
||||||
while (auto cur = backend_->cache().getSuccessor(prev, sequence))
|
|
||||||
{
|
|
||||||
assert(cur);
|
|
||||||
if (prev == Backend::firstKey)
|
|
||||||
{
|
{
|
||||||
backend_->writeSuccessor(
|
log_.debug()
|
||||||
uint256ToString(prev),
|
<< "Writing edge key = " << ripple::strHex(key);
|
||||||
sequence,
|
auto succ = backend_->cache().getSuccessor(
|
||||||
uint256ToString(cur->key));
|
*ripple::uint256::fromVoidChecked(key), sequence);
|
||||||
|
if (succ)
|
||||||
|
backend_->writeSuccessor(
|
||||||
|
std::move(key),
|
||||||
|
sequence,
|
||||||
|
uint256ToString(succ->key));
|
||||||
}
|
}
|
||||||
|
ripple::uint256 prev = Backend::firstKey;
|
||||||
if (isBookDir(cur->key, cur->blob))
|
while (auto cur =
|
||||||
|
backend_->cache().getSuccessor(prev, sequence))
|
||||||
{
|
{
|
||||||
auto base = getBookBase(cur->key);
|
assert(cur);
|
||||||
// make sure the base is not an actual object
|
if (prev == Backend::firstKey)
|
||||||
if (!backend_->cache().get(cur->key, sequence))
|
|
||||||
{
|
{
|
||||||
auto succ =
|
backend_->writeSuccessor(
|
||||||
backend_->cache().getSuccessor(base, sequence);
|
uint256ToString(prev),
|
||||||
assert(succ);
|
sequence,
|
||||||
if (succ->key == cur->key)
|
uint256ToString(cur->key));
|
||||||
{
|
|
||||||
log_.debug() << "Writing book successor = "
|
|
||||||
<< ripple::strHex(base) << " - "
|
|
||||||
<< ripple::strHex(cur->key);
|
|
||||||
|
|
||||||
backend_->writeSuccessor(
|
|
||||||
uint256ToString(base),
|
|
||||||
sequence,
|
|
||||||
uint256ToString(cur->key));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
++numWrites;
|
|
||||||
|
if (isBookDir(cur->key, cur->blob))
|
||||||
|
{
|
||||||
|
auto base = getBookBase(cur->key);
|
||||||
|
// make sure the base is not an actual object
|
||||||
|
if (!backend_->cache().get(cur->key, sequence))
|
||||||
|
{
|
||||||
|
auto succ =
|
||||||
|
backend_->cache().getSuccessor(base, sequence);
|
||||||
|
assert(succ);
|
||||||
|
if (succ->key == cur->key)
|
||||||
|
{
|
||||||
|
log_.debug() << "Writing book successor = "
|
||||||
|
<< ripple::strHex(base) << " - "
|
||||||
|
<< ripple::strHex(cur->key);
|
||||||
|
|
||||||
|
backend_->writeSuccessor(
|
||||||
|
uint256ToString(base),
|
||||||
|
sequence,
|
||||||
|
uint256ToString(cur->key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++numWrites;
|
||||||
|
}
|
||||||
|
prev = std::move(cur->key);
|
||||||
|
if (numWrites % 100000 == 0 && numWrites != 0)
|
||||||
|
log_.info()
|
||||||
|
<< "Wrote " << numWrites << " book successors";
|
||||||
}
|
}
|
||||||
prev = std::move(cur->key);
|
|
||||||
if (numWrites % 100000 == 0 && numWrites != 0)
|
|
||||||
log_.info() << "Wrote " << numWrites << " book successors";
|
|
||||||
}
|
|
||||||
|
|
||||||
backend_->writeSuccessor(
|
backend_->writeSuccessor(
|
||||||
uint256ToString(prev),
|
uint256ToString(prev),
|
||||||
sequence,
|
sequence,
|
||||||
uint256ToString(Backend::lastKey));
|
uint256ToString(Backend::lastKey));
|
||||||
|
|
||||||
++numWrites;
|
++numWrites;
|
||||||
auto end = std::chrono::system_clock::now();
|
});
|
||||||
auto seconds =
|
|
||||||
std::chrono::duration_cast<std::chrono::seconds>(end - start)
|
|
||||||
.count();
|
|
||||||
log_.info()
|
log_.info()
|
||||||
<< "Looping through cache and submitting all writes took "
|
<< "Looping through cache and submitting all writes took "
|
||||||
<< seconds
|
<< seconds
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <etl/ReportingETL.h>
|
#include <etl/ReportingETL.h>
|
||||||
#include <log/Logger.h>
|
#include <log/Logger.h>
|
||||||
#include <subscriptions/SubscriptionManager.h>
|
#include <subscriptions/SubscriptionManager.h>
|
||||||
|
#include <util/Profiler.h>
|
||||||
|
|
||||||
#include <boost/asio/connect.hpp>
|
#include <boost/asio/connect.hpp>
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
@@ -137,40 +138,39 @@ ReportingETL::loadInitialLedger(uint32_t startingSequence)
|
|||||||
|
|
||||||
log_.debug() << "Deserialized ledger header. " << detail::toString(lgrInfo);
|
log_.debug() << "Deserialized ledger header. " << detail::toString(lgrInfo);
|
||||||
|
|
||||||
auto start = std::chrono::system_clock::now();
|
auto timeDiff = util::timed<std::chrono::duration<double>>([&]() {
|
||||||
|
backend_->startWrites();
|
||||||
|
|
||||||
backend_->startWrites();
|
log_.debug() << "Started writes";
|
||||||
|
|
||||||
log_.debug() << "Started writes";
|
backend_->writeLedger(
|
||||||
|
lgrInfo, std::move(*ledgerData->mutable_ledger_header()));
|
||||||
|
|
||||||
backend_->writeLedger(
|
log_.debug() << "Wrote ledger";
|
||||||
lgrInfo, std::move(*ledgerData->mutable_ledger_header()));
|
FormattedTransactionsData insertTxResult =
|
||||||
|
insertTransactions(lgrInfo, *ledgerData);
|
||||||
|
log_.debug() << "Inserted txns";
|
||||||
|
|
||||||
log_.debug() << "Wrote ledger";
|
// download the full account state map. This function downloads full
|
||||||
FormattedTransactionsData insertTxResult =
|
// ledger data and pushes the downloaded data into the writeQueue.
|
||||||
insertTransactions(lgrInfo, *ledgerData);
|
// asyncWriter consumes from the queue and inserts the data into the
|
||||||
log_.debug() << "Inserted txns";
|
// Ledger object. Once the below call returns, all data has been pushed
|
||||||
|
// into the queue
|
||||||
|
loadBalancer_->loadInitialLedger(startingSequence);
|
||||||
|
|
||||||
// download the full account state map. This function downloads full ledger
|
log_.debug() << "Loaded initial ledger";
|
||||||
// data and pushes the downloaded data into the writeQueue. asyncWriter
|
|
||||||
// consumes from the queue and inserts the data into the Ledger object.
|
|
||||||
// Once the below call returns, all data has been pushed into the queue
|
|
||||||
loadBalancer_->loadInitialLedger(startingSequence);
|
|
||||||
|
|
||||||
log_.debug() << "Loaded initial ledger";
|
if (!stopping_)
|
||||||
|
{
|
||||||
if (!stopping_)
|
backend_->writeAccountTransactions(
|
||||||
{
|
std::move(insertTxResult.accountTxData));
|
||||||
backend_->writeAccountTransactions(
|
backend_->writeNFTs(std::move(insertTxResult.nfTokensData));
|
||||||
std::move(insertTxResult.accountTxData));
|
backend_->writeNFTTransactions(
|
||||||
backend_->writeNFTs(std::move(insertTxResult.nfTokensData));
|
std::move(insertTxResult.nfTokenTxData));
|
||||||
backend_->writeNFTTransactions(std::move(insertTxResult.nfTokenTxData));
|
}
|
||||||
}
|
backend_->finishWrites(startingSequence);
|
||||||
backend_->finishWrites(startingSequence);
|
});
|
||||||
|
log_.debug() << "Time to download and store ledger = " << timeDiff;
|
||||||
auto end = std::chrono::system_clock::now();
|
|
||||||
log_.debug() << "Time to download and store ledger = "
|
|
||||||
<< ((end - start).count()) / 1000000000.0;
|
|
||||||
return lgrInfo;
|
return lgrInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,10 +530,8 @@ ReportingETL::buildNextLedger(org::xrpl::rpc::v1::GetLedgerResponse& rawData)
|
|||||||
backend_->writeNFTTransactions(std::move(insertTxResult.nfTokenTxData));
|
backend_->writeNFTTransactions(std::move(insertTxResult.nfTokenTxData));
|
||||||
log_.debug() << "wrote account_tx";
|
log_.debug() << "wrote account_tx";
|
||||||
|
|
||||||
auto start = std::chrono::system_clock::now();
|
auto [success, duration] = util::timed<std::chrono::duration<double>>(
|
||||||
bool success = backend_->finishWrites(lgrInfo.seq);
|
[&]() { return backend_->finishWrites(lgrInfo.seq); });
|
||||||
auto end = std::chrono::system_clock::now();
|
|
||||||
auto duration = ((end - start).count()) / 1000000000.0;
|
|
||||||
|
|
||||||
log_.debug() << "Finished writes. took " << std::to_string(duration);
|
log_.debug() << "Finished writes. took " << std::to_string(duration);
|
||||||
log_.debug() << "Finished ledger update. " << detail::toString(lgrInfo);
|
log_.debug() << "Finished ledger update. " << detail::toString(lgrInfo);
|
||||||
@@ -620,12 +618,10 @@ ReportingETL::runETLPipeline(uint32_t startSequence, int numExtractors)
|
|||||||
currentSequence) &&
|
currentSequence) &&
|
||||||
!writeConflict && !isStopping())
|
!writeConflict && !isStopping())
|
||||||
{
|
{
|
||||||
auto start = std::chrono::system_clock::now();
|
auto [fetchResponse, time] =
|
||||||
std::optional<org::xrpl::rpc::v1::GetLedgerResponse>
|
util::timed<std::chrono::duration<double>>([&]() {
|
||||||
fetchResponse{fetchLedgerDataAndDiff(currentSequence)};
|
return fetchLedgerDataAndDiff(currentSequence);
|
||||||
auto end = std::chrono::system_clock::now();
|
});
|
||||||
|
|
||||||
auto time = ((end - start).count()) / 1000000000.0;
|
|
||||||
totalTime += time;
|
totalTime += time;
|
||||||
|
|
||||||
// if the fetch is unsuccessful, stop. fetchLedger only
|
// if the fetch is unsuccessful, stop. fetchLedger only
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <backend/BackendInterface.h>
|
#include <backend/BackendInterface.h>
|
||||||
#include <log/Logger.h>
|
#include <log/Logger.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <util/Profiler.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
@@ -798,14 +799,10 @@ traverseOwnedNodes(
|
|||||||
.count()
|
.count()
|
||||||
<< " milliseconds";
|
<< " milliseconds";
|
||||||
|
|
||||||
start = std::chrono::system_clock::now();
|
auto [objects, timeDiff] = util::timed(
|
||||||
auto objects = backend.fetchLedgerObjects(keys, sequence, yield);
|
[&]() { return backend.fetchLedgerObjects(keys, sequence, yield); });
|
||||||
end = std::chrono::system_clock::now();
|
|
||||||
|
|
||||||
gLog.debug() << "Time loading owned entries: "
|
gLog.debug() << "Time loading owned entries: " << timeDiff
|
||||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
||||||
end - start)
|
|
||||||
.count()
|
|
||||||
<< " milliseconds";
|
<< " milliseconds";
|
||||||
|
|
||||||
for (auto i = 0; i < objects.size(); ++i)
|
for (auto i = 0; i < objects.size(); ++i)
|
||||||
@@ -1652,60 +1649,58 @@ traverseTransactions(
|
|||||||
boost::json::array txns;
|
boost::json::array txns;
|
||||||
auto [blobs, retCursor] = transactionFetcher(
|
auto [blobs, retCursor] = transactionFetcher(
|
||||||
context.backend, limit, forward, cursor, context.yield);
|
context.backend, limit, forward, cursor, context.yield);
|
||||||
auto serializationStart = std::chrono::system_clock::now();
|
auto timeDiff = util::timed([&, &retCursor = retCursor, &blobs = blobs]() {
|
||||||
|
if (retCursor)
|
||||||
if (retCursor)
|
|
||||||
{
|
|
||||||
boost::json::object cursorJson;
|
|
||||||
cursorJson[JS(ledger)] = retCursor->ledgerSequence;
|
|
||||||
cursorJson[JS(seq)] = retCursor->transactionIndex;
|
|
||||||
response[JS(marker)] = cursorJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const& txnPlusMeta : blobs)
|
|
||||||
{
|
|
||||||
if ((txnPlusMeta.ledgerSequence < minIndex && !forward) ||
|
|
||||||
(txnPlusMeta.ledgerSequence > maxIndex && forward))
|
|
||||||
{
|
{
|
||||||
response.erase(JS(marker));
|
boost::json::object cursorJson;
|
||||||
break;
|
cursorJson[JS(ledger)] = retCursor->ledgerSequence;
|
||||||
}
|
cursorJson[JS(seq)] = retCursor->transactionIndex;
|
||||||
else if (txnPlusMeta.ledgerSequence > maxIndex && !forward)
|
response[JS(marker)] = cursorJson;
|
||||||
{
|
|
||||||
gLog.debug() << "Skipping over transactions from incomplete ledger";
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::json::object obj;
|
for (auto const& txnPlusMeta : blobs)
|
||||||
|
|
||||||
if (!binary)
|
|
||||||
{
|
{
|
||||||
auto [txn, meta] = toExpandedJson(txnPlusMeta);
|
if ((txnPlusMeta.ledgerSequence < minIndex && !forward) ||
|
||||||
obj[JS(meta)] = meta;
|
(txnPlusMeta.ledgerSequence > maxIndex && forward))
|
||||||
obj[JS(tx)] = txn;
|
{
|
||||||
obj[JS(tx)].as_object()[JS(ledger_index)] =
|
response.erase(JS(marker));
|
||||||
txnPlusMeta.ledgerSequence;
|
break;
|
||||||
obj[JS(tx)].as_object()[JS(date)] = txnPlusMeta.date;
|
}
|
||||||
}
|
else if (txnPlusMeta.ledgerSequence > maxIndex && !forward)
|
||||||
else
|
{
|
||||||
{
|
gLog.debug()
|
||||||
obj[JS(meta)] = ripple::strHex(txnPlusMeta.metadata);
|
<< "Skipping over transactions from incomplete ledger";
|
||||||
obj[JS(tx_blob)] = ripple::strHex(txnPlusMeta.transaction);
|
continue;
|
||||||
obj[JS(ledger_index)] = txnPlusMeta.ledgerSequence;
|
}
|
||||||
obj[JS(date)] = txnPlusMeta.date;
|
|
||||||
}
|
|
||||||
obj[JS(validated)] = true;
|
|
||||||
txns.push_back(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
response[JS(ledger_index_min)] = minIndex;
|
boost::json::object obj;
|
||||||
response[JS(ledger_index_max)] = maxIndex;
|
|
||||||
response[JS(transactions)] = txns;
|
if (!binary)
|
||||||
|
{
|
||||||
|
auto [txn, meta] = toExpandedJson(txnPlusMeta);
|
||||||
|
obj[JS(meta)] = meta;
|
||||||
|
obj[JS(tx)] = txn;
|
||||||
|
obj[JS(tx)].as_object()[JS(ledger_index)] =
|
||||||
|
txnPlusMeta.ledgerSequence;
|
||||||
|
obj[JS(tx)].as_object()[JS(date)] = txnPlusMeta.date;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
obj[JS(meta)] = ripple::strHex(txnPlusMeta.metadata);
|
||||||
|
obj[JS(tx_blob)] = ripple::strHex(txnPlusMeta.transaction);
|
||||||
|
obj[JS(ledger_index)] = txnPlusMeta.ledgerSequence;
|
||||||
|
obj[JS(date)] = txnPlusMeta.date;
|
||||||
|
}
|
||||||
|
obj[JS(validated)] = true;
|
||||||
|
txns.push_back(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
response[JS(ledger_index_min)] = minIndex;
|
||||||
|
response[JS(ledger_index_max)] = maxIndex;
|
||||||
|
response[JS(transactions)] = txns;
|
||||||
|
});
|
||||||
|
gLog.info() << "serialization took " << timeDiff
|
||||||
|
|
||||||
gLog.info() << "serialization took "
|
|
||||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
||||||
std::chrono::system_clock::now() - serializationStart)
|
|
||||||
.count()
|
|
||||||
<< " milliseconds";
|
<< " milliseconds";
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <log/Logger.h>
|
#include <log/Logger.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <util/Profiler.h>
|
||||||
|
|
||||||
using namespace clio;
|
using namespace clio;
|
||||||
|
|
||||||
@@ -37,27 +38,23 @@ doAccountTx(Context const& context)
|
|||||||
return status;
|
return status;
|
||||||
|
|
||||||
constexpr std::string_view outerFuncName = __func__;
|
constexpr std::string_view outerFuncName = __func__;
|
||||||
auto const maybeResponse =
|
auto const maybeResponse = traverseTransactions(
|
||||||
traverseTransactions(
|
context,
|
||||||
context,
|
[&accountID, &outerFuncName](
|
||||||
[&accountID, &outerFuncName](
|
std::shared_ptr<Backend::BackendInterface const> const& backend,
|
||||||
std::shared_ptr<Backend::BackendInterface const> const& backend,
|
std::uint32_t const limit,
|
||||||
std::uint32_t const limit,
|
bool const forward,
|
||||||
bool const forward,
|
std::optional<Backend::TransactionsCursor> const& cursorIn,
|
||||||
std::optional<Backend::TransactionsCursor> const& cursorIn,
|
boost::asio::yield_context& yield) {
|
||||||
boost::asio::yield_context& yield) {
|
auto [txnsAndCursor, timeDiff] = util::timed([&]() {
|
||||||
auto const start = std::chrono::system_clock::now();
|
return backend->fetchAccountTransactions(
|
||||||
auto const txnsAndCursor = backend->fetchAccountTransactions(
|
|
||||||
accountID, limit, forward, cursorIn, yield);
|
accountID, limit, forward, cursorIn, yield);
|
||||||
gLog.info()
|
|
||||||
<< outerFuncName << " db fetch took "
|
|
||||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
||||||
std::chrono::system_clock::now() - start)
|
|
||||||
.count()
|
|
||||||
<< " milliseconds - num blobs = "
|
|
||||||
<< txnsAndCursor.txns.size();
|
|
||||||
return txnsAndCursor;
|
|
||||||
});
|
});
|
||||||
|
gLog.info() << outerFuncName << " db fetch took " << timeDiff
|
||||||
|
<< " milliseconds - num blobs = "
|
||||||
|
<< txnsAndCursor.txns.size();
|
||||||
|
return txnsAndCursor;
|
||||||
|
});
|
||||||
|
|
||||||
if (auto const status = std::get_if<Status>(&maybeResponse); status)
|
if (auto const status = std::get_if<Status>(&maybeResponse); status)
|
||||||
return *status;
|
return *status;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <log/Logger.h>
|
#include <log/Logger.h>
|
||||||
#include <rpc/RPCHelpers.h>
|
#include <rpc/RPCHelpers.h>
|
||||||
|
#include <util/Profiler.h>
|
||||||
|
|
||||||
using namespace clio;
|
using namespace clio;
|
||||||
|
|
||||||
@@ -38,28 +39,25 @@ doNFTHistory(Context const& context)
|
|||||||
auto const tokenID = std::get<ripple::uint256>(maybeTokenID);
|
auto const tokenID = std::get<ripple::uint256>(maybeTokenID);
|
||||||
|
|
||||||
constexpr std::string_view outerFuncName = __func__;
|
constexpr std::string_view outerFuncName = __func__;
|
||||||
auto const maybeResponse =
|
auto const maybeResponse = traverseTransactions(
|
||||||
traverseTransactions(
|
context,
|
||||||
context,
|
[&tokenID, &outerFuncName](
|
||||||
[&tokenID, &outerFuncName](
|
std::shared_ptr<Backend::BackendInterface const> const& backend,
|
||||||
std::shared_ptr<Backend::BackendInterface const> const& backend,
|
std::uint32_t const limit,
|
||||||
std::uint32_t const limit,
|
bool const forward,
|
||||||
bool const forward,
|
std::optional<Backend::TransactionsCursor> const& cursorIn,
|
||||||
std::optional<Backend::TransactionsCursor> const& cursorIn,
|
boost::asio::yield_context& yield)
|
||||||
boost::asio::yield_context& yield)
|
-> Backend::TransactionsAndCursor {
|
||||||
-> Backend::TransactionsAndCursor {
|
auto const [txnsAndCursor, timeDiff] =
|
||||||
auto const start = std::chrono::system_clock::now();
|
util::timed([&, &tokenID = tokenID]() {
|
||||||
auto const txnsAndCursor = backend->fetchNFTTransactions(
|
return backend->fetchNFTTransactions(
|
||||||
tokenID, limit, forward, cursorIn, yield);
|
tokenID, limit, forward, cursorIn, yield);
|
||||||
gLog.info()
|
});
|
||||||
<< outerFuncName << " db fetch took "
|
gLog.info() << outerFuncName << " db fetch took " << timeDiff
|
||||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
<< " milliseconds - num blobs = "
|
||||||
std::chrono::system_clock::now() - start)
|
<< txnsAndCursor.txns.size();
|
||||||
.count()
|
return txnsAndCursor;
|
||||||
<< " milliseconds - num blobs = "
|
});
|
||||||
<< txnsAndCursor.txns.size();
|
|
||||||
return txnsAndCursor;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (auto const status = std::get_if<Status>(&maybeResponse); status)
|
if (auto const status = std::get_if<Status>(&maybeResponse); status)
|
||||||
return *status;
|
return *status;
|
||||||
|
|||||||
59
src/util/Profiler.h
Normal file
59
src/util/Profiler.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2022, the clio developers.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Profiler function to measure the time consuming
|
||||||
|
* @param func function object, can be a lamdba or function wrapper
|
||||||
|
* @return return a pair if function wrapper has return value: result of
|
||||||
|
* function wrapper and the elapsed time(ms) during executing the given
|
||||||
|
* function only return the elapsed time if function wrapper does not have
|
||||||
|
* return value
|
||||||
|
*/
|
||||||
|
template <typename U = std::chrono::milliseconds, typename F>
|
||||||
|
[[nodiscard]] auto
|
||||||
|
timed(F&& func)
|
||||||
|
{
|
||||||
|
auto start = std::chrono::system_clock::now();
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<decltype(func()), void>)
|
||||||
|
{
|
||||||
|
func();
|
||||||
|
return std::chrono::duration_cast<U>(
|
||||||
|
std::chrono::system_clock::now() - start)
|
||||||
|
.count();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto ret = func();
|
||||||
|
auto elapsed = std::chrono::duration_cast<U>(
|
||||||
|
std::chrono::system_clock::now() - start)
|
||||||
|
.count();
|
||||||
|
return std::make_pair(ret, elapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
@@ -108,7 +108,6 @@ public:
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto it = ipConnCount_.find(ip);
|
auto it = ipConnCount_.find(ip);
|
||||||
assert(it != ipConnCount_.end());
|
|
||||||
if (it != ipConnCount_.end())
|
if (it != ipConnCount_.end())
|
||||||
{
|
{
|
||||||
connsOk = it->second <= maxConnCount_;
|
connsOk = it->second <= maxConnCount_;
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
#include <rpc/Counters.h>
|
#include <rpc/Counters.h>
|
||||||
#include <rpc/RPC.h>
|
#include <rpc/RPC.h>
|
||||||
#include <rpc/WorkQueue.h>
|
#include <rpc/WorkQueue.h>
|
||||||
|
#include <util/Profiler.h>
|
||||||
#include <util/Taggable.h>
|
#include <util/Taggable.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <webserver/DOSGuard.h>
|
#include <webserver/DOSGuard.h>
|
||||||
@@ -437,11 +438,10 @@ handle_request(
|
|||||||
RPC::makeError(RPC::RippledError::rpcBAD_SYNTAX))));
|
RPC::makeError(RPC::RippledError::rpcBAD_SYNTAX))));
|
||||||
|
|
||||||
boost::json::object response;
|
boost::json::object response;
|
||||||
auto start = std::chrono::system_clock::now();
|
auto [v, timeDiff] =
|
||||||
auto v = RPC::buildResponse(*context);
|
util::timed([&]() { return RPC::buildResponse(*context); });
|
||||||
auto end = std::chrono::system_clock::now();
|
|
||||||
auto us =
|
auto us = std::chrono::duration<int, std::milli>(timeDiff);
|
||||||
std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
|
||||||
RPC::logDuration(*context, us);
|
RPC::logDuration(*context, us);
|
||||||
|
|
||||||
if (auto status = std::get_if<RPC::Status>(&v))
|
if (auto status = std::get_if<RPC::Status>(&v))
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include <rpc/WorkQueue.h>
|
#include <rpc/WorkQueue.h>
|
||||||
#include <subscriptions/Message.h>
|
#include <subscriptions/Message.h>
|
||||||
#include <subscriptions/SubscriptionManager.h>
|
#include <subscriptions/SubscriptionManager.h>
|
||||||
|
#include <util/Profiler.h>
|
||||||
#include <util/Taggable.h>
|
#include <util/Taggable.h>
|
||||||
#include <webserver/DOSGuard.h>
|
#include <webserver/DOSGuard.h>
|
||||||
|
|
||||||
@@ -343,11 +344,10 @@ public:
|
|||||||
|
|
||||||
response = getDefaultWsResponse(id);
|
response = getDefaultWsResponse(id);
|
||||||
|
|
||||||
auto start = std::chrono::system_clock::now();
|
auto [v, timeDiff] =
|
||||||
auto v = RPC::buildResponse(*context);
|
util::timed([&]() { return RPC::buildResponse(*context); });
|
||||||
auto end = std::chrono::system_clock::now();
|
|
||||||
auto us = std::chrono::duration_cast<std::chrono::microseconds>(
|
auto us = std::chrono::duration<int, std::milli>(timeDiff);
|
||||||
end - start);
|
|
||||||
logDuration(*context, us);
|
logDuration(*context, us);
|
||||||
|
|
||||||
if (auto status = std::get_if<RPC::Status>(&v))
|
if (auto status = std::get_if<RPC::Status>(&v))
|
||||||
|
|||||||
108
unittests/ProfilerTest.cpp
Normal file
108
unittests/ProfilerTest.cpp
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2022, the clio developers.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <util/Profiler.h>
|
||||||
|
|
||||||
|
using namespace util;
|
||||||
|
TEST(TimedTest, HasReturnValue)
|
||||||
|
{
|
||||||
|
auto [ret, time] = timed([]() {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||||
|
return 8;
|
||||||
|
});
|
||||||
|
|
||||||
|
ASSERT_EQ(ret, 8);
|
||||||
|
ASSERT_NE(time, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TimedTest, ReturnVoid)
|
||||||
|
{
|
||||||
|
auto time = timed(
|
||||||
|
[]() { std::this_thread::sleep_for(std::chrono::milliseconds(5)); });
|
||||||
|
|
||||||
|
ASSERT_NE(time, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FunctorTest
|
||||||
|
{
|
||||||
|
void
|
||||||
|
operator()() const
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(TimedTest, Functor)
|
||||||
|
{
|
||||||
|
auto time = timed(FunctorTest());
|
||||||
|
|
||||||
|
ASSERT_NE(time, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TimedTest, MovedLambda)
|
||||||
|
{
|
||||||
|
auto f = []() {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||||
|
return 8;
|
||||||
|
};
|
||||||
|
auto [ret, time] = timed(std::move(f));
|
||||||
|
|
||||||
|
ASSERT_EQ(ret, 8);
|
||||||
|
ASSERT_NE(time, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TimedTest, ChangeToNs)
|
||||||
|
{
|
||||||
|
auto f = []() {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||||
|
return 8;
|
||||||
|
};
|
||||||
|
auto [ret, time] = timed<std::chrono::nanoseconds>(std::move(f));
|
||||||
|
ASSERT_EQ(ret, 8);
|
||||||
|
ASSERT_GE(time, 5 * 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TimedTest, NestedLambda)
|
||||||
|
{
|
||||||
|
double timeNested;
|
||||||
|
auto f = [&]() {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||||
|
timeNested = timed([]() {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||||
|
});
|
||||||
|
return 8;
|
||||||
|
};
|
||||||
|
auto [ret, time] = timed<std::chrono::nanoseconds>(std::move(f));
|
||||||
|
ASSERT_EQ(ret, 8);
|
||||||
|
ASSERT_GE(timeNested, 5);
|
||||||
|
ASSERT_GE(time, 10 * 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TimedTest, FloatSec)
|
||||||
|
{
|
||||||
|
auto f = []() {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||||
|
return 8;
|
||||||
|
};
|
||||||
|
auto [ret, time] = timed<std::chrono::duration<double>>(std::move(f));
|
||||||
|
ASSERT_EQ(ret, 8);
|
||||||
|
ASSERT_GE(time, 0);
|
||||||
|
}
|
||||||
@@ -127,6 +127,7 @@ struct AsyncAsioContextTest : public NoLoggerFixture
|
|||||||
{
|
{
|
||||||
work.reset();
|
work.reset();
|
||||||
ctx.stop();
|
ctx.stop();
|
||||||
|
runner.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -134,7 +135,7 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::optional<boost::asio::io_service::work> work;
|
std::optional<boost::asio::io_service::work> work;
|
||||||
std::jthread runner{[this] { ctx.run(); }};
|
std::thread runner{[this] { ctx.run(); }};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user