add server info

This commit is contained in:
CJ Cobb
2021-06-01 13:33:52 -04:00
parent 46d4ee4548
commit d692f7f675
17 changed files with 200 additions and 61 deletions

View File

@@ -82,7 +82,8 @@ target_sources(reporting PRIVATE
handlers/BookOffers.cpp
handlers/LedgerRange.cpp
handlers/Ledger.cpp
handlers/LedgerEntry.cpp)
handlers/LedgerEntry.cpp
handlers/ServerInfo.cpp)
message(${Boost_LIBRARIES})

View File

@@ -104,7 +104,7 @@ doAccountInfo(
{
response["success"] = "fetched successfully!";
if (!binary)
response["object"] = getJson(sle);
response["object"] = toJson(sle);
else
response["object"] = ripple::strHex(*dbResponse);
response["db_time"] = time;
@@ -124,7 +124,7 @@ doAccountInfo(
// support multiple SignerLists on one account.
auto const sleSigners = ledger->read(keylet::signers(accountID));
if (sleSigners)
jvSignerList.append(sleSigners->getJson(JsonOptions::none));
jvSignerList.append(sleSigners->toJson(JsonOptions::none));
result[jss::account_data][jss::signer_lists] =
std::move(jvSignerList);

View File

@@ -110,8 +110,8 @@ doAccountTx(boost::json::object const& request, BackendInterface const& backend)
if (!binary)
{
auto [txn, meta] = deserializeTxPlusMeta(txnPlusMeta);
obj["transaction"] = getJson(*txn);
obj["metadata"] = getJson(*meta);
obj["transaction"] = toJson(*txn);
obj["metadata"] = toJson(*meta);
}
else
{

View File

@@ -304,7 +304,7 @@ doBookOffers(
ripple::SLE offer{it, obj.key};
ripple::uint256 bookDir = offer.getFieldH256(ripple::sfBookDirectory);
boost::json::object offerJson = getJson(offer);
boost::json::object offerJson = toJson(offer);
offerJson["quality"] = ripple::amountFromQuality(getQuality(bookDir)).getText();
jsonOffers.push_back(offerJson);
}

View File

@@ -37,19 +37,7 @@ doLedger(boost::json::object const& request, BackendInterface const& backend)
}
else
{
header["ledger_sequence"] = lgrInfo->seq;
header["ledger_hash"] = ripple::strHex(lgrInfo->hash);
header["txns_hash"] = ripple::strHex(lgrInfo->txHash);
header["state_hash"] = ripple::strHex(lgrInfo->accountHash);
header["parent_hash"] = ripple::strHex(lgrInfo->parentHash);
header["total_coins"] = ripple::to_string(lgrInfo->drops);
header["close_flags"] = lgrInfo->closeFlags;
// Always show fields that contribute to the ledger hash
header["parent_close_time"] =
lgrInfo->parentCloseTime.time_since_epoch().count();
header["close_time"] = lgrInfo->closeTime.time_since_epoch().count();
header["close_time_resolution"] = lgrInfo->closeTimeResolution.count();
header = toJson(*lgrInfo);
}
response["header"] = header;
if (getTransactions)
@@ -70,8 +58,8 @@ doLedger(boost::json::object const& request, BackendInterface const& backend)
if (!binary)
{
auto [sttx, meta] = deserializeTxPlusMeta(obj);
entry["transaction"] = getJson(*sttx);
entry["metadata"] = getJson(*meta);
entry["transaction"] = toJson(*sttx);
entry["metadata"] = toJson(*meta);
}
else
{

View File

@@ -59,7 +59,12 @@ doLedgerData(
{
BOOST_LOG_TRIVIAL(debug) << __func__ << " : parsing cursor";
cursor = ripple::uint256{};
cursor->parseHex(request.at("cursor").as_string().c_str());
if (!cursor->parseHex(request.at("cursor").as_string().c_str()))
{
response["error"] = "Invalid cursor";
response["request"] = request;
return response;
}
}
bool binary =
request.contains("binary") ? request.at("binary").as_bool() : false;
@@ -91,7 +96,7 @@ doLedgerData(
objects.push_back(entry);
}
else
objects.push_back(getJson(sle));
objects.push_back(toJson(sle));
}
response["objects"] = objects;
if (returnedCursor)

View File

@@ -47,7 +47,7 @@ doLedgerEntry(
{
ripple::STLedgerEntry sle{
ripple::SerialIter{dbResponse->data(), dbResponse->size()}, key};
response["object"] = getJson(sle);
response["object"] = toJson(sle);
}
return response;

View File

@@ -42,7 +42,7 @@ deserializeTxPlusMeta(Backend::TransactionAndMetadata const& blobs)
}
boost::json::object
getJson(ripple::STBase const& obj)
toJson(ripple::STBase const& obj)
{
auto start = std::chrono::system_clock::now();
boost::json::value value = boost::json::parse(
@@ -55,7 +55,7 @@ getJson(ripple::STBase const& obj)
}
boost::json::object
getJson(ripple::SLE const& sle)
toJson(ripple::SLE const& sle)
{
auto start = std::chrono::system_clock::now();
boost::json::value value = boost::json::parse(
@@ -66,6 +66,27 @@ getJson(ripple::SLE const& sle)
.count();
return value.as_object();
}
boost::json::object
toJson(ripple::LedgerInfo const& lgrInfo)
{
boost::json::object header;
header["ledger_sequence"] = lgrInfo.seq;
header["ledger_hash"] = ripple::strHex(lgrInfo.hash);
header["txns_hash"] = ripple::strHex(lgrInfo.txHash);
header["state_hash"] = ripple::strHex(lgrInfo.accountHash);
header["parent_hash"] = ripple::strHex(lgrInfo.parentHash);
header["total_coins"] = ripple::to_string(lgrInfo.drops);
header["close_flags"] = lgrInfo.closeFlags;
// Always show fields that contribute to the ledger hash
header["parent_close_time"] =
lgrInfo.parentCloseTime.time_since_epoch().count();
header["close_time"] = lgrInfo.closeTime.time_since_epoch().count();
header["close_time_resolution"] = lgrInfo.closeTimeResolution.count();
return header;
}
std::optional<uint32_t>
ledgerSequenceFromRequest(
boost::json::object const& request,

View File

@@ -15,15 +15,19 @@ std::pair<
deserializeTxPlusMeta(Backend::TransactionAndMetadata const& blobs);
boost::json::object
getJson(ripple::STBase const& obj);
toJson(ripple::STBase const& obj);
boost::json::object
getJson(ripple::SLE const& sle);
toJson(ripple::SLE const& sle);
boost::json::object
toJson(ripple::LedgerInfo const& info);
std::optional<uint32_t>
ledgerSequenceFromRequest(
boost::json::object const& request,
BackendInterface const& backend);
std::vector<unsigned char>
ledgerInfoToBlob(ripple::LedgerInfo const& info);

54
handlers/ServerInfo.cpp Normal file
View File

@@ -0,0 +1,54 @@
#include <handlers/RPCHelpers.h>
#include <reporting/BackendInterface.h>
boost::json::object
doServerInfo(
boost::json::object const& request,
BackendInterface const& backend)
{
boost::json::object response;
auto rng = backend.fetchLedgerRange();
if (!rng)
{
response["complete_ledgers"] = "empty";
}
else
{
std::string completeLedgers = std::to_string(rng->minSequence);
if (rng->maxSequence != rng->minSequence)
completeLedgers += "-" + std::to_string(rng->maxSequence);
response["complete_ledgers"] = completeLedgers;
}
if (rng)
{
auto lgrInfo = backend.fetchLedgerBySequence(rng->maxSequence);
response["validated_ledger"] = toJson(*lgrInfo);
}
boost::json::array indexes;
if (rng)
{
uint32_t cur = rng->minSequence;
while (cur <= rng->maxSequence + 1)
{
auto keyIndex = backend.getKeyIndexOfSeq(cur);
assert(keyIndex.has_value());
cur = keyIndex->keyIndex;
auto page = backend.fetchLedgerPage({}, cur, 1);
boost::json::object entry;
entry["complete"] = page.warning.has_value();
entry["sequence"] = cur;
indexes.emplace_back(entry);
cur = cur + 1;
}
}
response["indexes"] = indexes;
auto indexing = backend.getIndexer().getCurrentlyIndexing();
if (indexing)
response["indexing"] = *indexing;
else
response["indexing"] = "none";
return response;
}

View File

@@ -63,8 +63,8 @@ doTx(boost::json::object const& request, BackendInterface const& backend)
if (!binary)
{
auto [sttx, meta] = deserializeTxPlusMeta(dbResponse.value());
response["transaction"] = getJson(*sttx);
response["metadata"] = getJson(*meta);
response["transaction"] = toJson(*sttx);
response["metadata"] = toJson(*meta);
}
else
{

View File

@@ -10,7 +10,6 @@ BackendIndexer::BackendIndexer(boost::json::object const& config)
};
BackendIndexer::~BackendIndexer()
{
std::unique_lock lck(mutex_);
work_.reset();
ioThread_.join();
}
@@ -114,6 +113,21 @@ BackendIndexer::writeKeyFlagLedgerAsync(
{
try
{
{
auto page =
backend.fetchLedgerPage({}, nextFlag.keyIndex, 1);
if (!page.warning)
{
BOOST_LOG_TRIVIAL(warning)
<< "writeKeyFlagLedger - "
<< "flag ledger already written. flag = "
<< std::to_string(nextFlag.keyIndex)
<< " , ledger sequence = "
<< std::to_string(ledgerSequence);
return;
}
}
indexing_ = nextFlag.keyIndex;
auto start = std::chrono::system_clock::now();
auto [objects, curCursor, warning] =
backend.fetchLedgerPage(cursor, ledgerSequence, 2048);
@@ -121,8 +135,6 @@ BackendIndexer::writeKeyFlagLedgerAsync(
// no cursor means this is the first page
if (!cursor)
{
// if there is no warning, we don't need to do a repair
// warning only shows up on the first page
if (warning)
{
BOOST_LOG_TRIVIAL(error)
@@ -176,6 +188,7 @@ BackendIndexer::writeKeyFlagLedgerAsync(
<< std::chrono::duration_cast<std::chrono::milliseconds>(
end - begin)
.count();
indexing_ = 0;
});
BOOST_LOG_TRIVIAL(info)
<< __func__
@@ -207,6 +220,10 @@ BackendIndexer::finish(uint32_t ledgerSequence, BackendInterface const& backend)
// write completion record
ripple::uint256 zero = {};
backend.writeKeys({zero}, keyIndex);
// write next flag sychronously
keyIndex = getKeyIndexOfSeq(ledgerSequence + 1);
backend.writeKeys(keys, keyIndex);
backend.writeKeys({zero}, keyIndex);
}
isFirst_ = false;
keys = {};

View File

@@ -84,6 +84,8 @@ class BackendIndexer
std::optional<boost::asio::io_context::work> work_;
std::thread ioThread_;
std::atomic_uint32_t indexing_ = 0;
uint32_t keyShift_ = 20;
std::unordered_set<ripple::uint256> keys;
@@ -115,6 +117,14 @@ public:
{
return keyShift_;
}
std::optional<uint32_t>
getCurrentlyIndexing()
{
uint32_t cur = indexing_.load();
if (cur != 0)
return cur;
return {};
}
KeyIndex
getKeyIndexOfSeq(uint32_t seq) const
{
@@ -171,16 +181,18 @@ public:
if (commitRes)
{
if (isFirst_)
{
auto rng = fetchLedgerRangeNoThrow();
if (rng && rng->minSequence != ledgerSequence)
isFirst_ = false;
indexer_.doKeysRepairAsync(*this, ledgerSequence);
}
if (indexer_.isKeyFlagLedger(ledgerSequence) || isFirst_)
if (indexer_.isKeyFlagLedger(ledgerSequence))
indexer_.writeKeyFlagLedgerAsync(ledgerSequence, *this);
isFirst_ = false;
}
else
{
// if commitRes is false, we are relinquishing control of ETL. We
// reset isFirst_ to true so that way if we later regain control of
// ETL, we trigger the index repair
isFirst_ = true;
}
return commitRes;
}

View File

@@ -212,6 +212,24 @@ public:
", grpc port : " + grpcPort_ + " }";
}
boost::json::object
toJson() const
{
boost::json::object res;
res["validated_range"] = getValidatedRange();
res["is_connected"] = std::to_string(isConnected());
res["ip"] = ip_;
res["ws_port"] = wsPort_;
res["grpc_port"] = grpcPort_;
auto last = getLastMsgTime();
if (last.time_since_epoch().count() != 0)
res["last_msg_arrival_time"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now() - getLastMsgTime())
.count());
return res;
}
/// Download a ledger in full
/// @param ledgerSequence sequence of the ledger to download
/// @param writeQueue queue to push downloaded ledger objects
@@ -341,16 +359,16 @@ public:
// forwarded. return true;
// }
// Json::Value
// toJson() const
// {
// Json::Value ret(Json::arrayValue);
// for (auto& src : sources_)
// {
// ret.append(src->toJson());
// }
// return ret;
// }
boost::json::array
toJson() const
{
boost::json::array ret;
for (auto& src : sources_)
{
ret.emplace_back(src->toJson());
}
return ret;
}
//
// /// Randomly select a p2p node to forward a gRPC request to
// /// @return gRPC stub to forward requests to p2p node

View File

@@ -190,6 +190,13 @@ ReportingETL::publishLedger(uint32_t ledgerSequence, uint32_t maxAttempts)
++numAttempts;
continue;
}
else
{
auto lgr =
flatMapBackend_->fetchLedgerBySequence(ledgerSequence);
assert(lgr);
publishLedger(*lgr);
}
}
catch (Backend::DatabaseTimeout const& e)
{

View File

@@ -279,21 +279,23 @@ public:
return numMarkers_;
}
/*
Json::Value
boost::json::object
getInfo()
{
Json::Value result(Json::objectValue);
boost::json::object result;
result["etl_sources"] = loadBalancer_.toJson();
result["is_writer"] = writing_.load();
result["read_only"] = readOnly_;
auto last = getLastPublish();
if (last.time_since_epoch().count() != 0)
result["last_publish_time"] = to_string(
date::floor<std::chrono::microseconds>(getLastPublish()));
result["last_publish_time"] = std::to_string(
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now() - getLastPublish())
.count());
return result;
}
*/
/// start all of the necessary components and begin ETL
void

View File

@@ -44,7 +44,8 @@ enum RPCCommand {
ledger_data,
book_offers,
ledger_range,
ledger_entry
ledger_entry,
server_info
};
std::unordered_map<std::string, RPCCommand> commandMap{
{"tx", tx},
@@ -54,7 +55,8 @@ std::unordered_map<std::string, RPCCommand> commandMap{
{"ledger_entry", ledger_entry},
{"account_info", account_info},
{"ledger_data", ledger_data},
{"book_offers", book_offers}};
{"book_offers", book_offers},
{"server_info", server_info}};
boost::json::object
doAccountInfo(
@@ -84,6 +86,10 @@ boost::json::object
doLedgerRange(
boost::json::object const& request,
BackendInterface const& backend);
boost::json::object
doServerInfo(
boost::json::object const& request,
BackendInterface const& backend);
std::pair<boost::json::object, uint32_t>
buildResponse(
@@ -125,6 +131,10 @@ buildResponse(
return {res, 1};
}
break;
case server_info: {
return {doServerInfo(request, backend), 1};
break;
}
case account_info:
return {doAccountInfo(request, backend), 1};
break;
@@ -170,10 +180,10 @@ public:
void
run()
{
// We need to be executing within a strand to perform async operations
// on the I/O objects in this session. Although not strictly necessary
// for single-threaded contexts, this example code is written to be
// thread-safe by default.
// We need to be executing within a strand to perform async
// operations on the I/O objects in this session. Although not
// strictly necessary for single-threaded contexts, this example
// code is written to be thread-safe by default.
boost::asio::dispatch(
ws_.get_executor(),
boost::beast::bind_front_handler(