0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-08 10:31:50 -05:00

rpc: make it possible to aggregate the result in getnetmsgstats

Aggregation can be by either one of direction, network, connection type
or message type. For example if the following

```
{
    "ipv4": { "ping": 3 },
    "ipv6": { "ping": 4 }
}
```

is aggregated by network, then the result will be

```
{
    "ping": 7
}
```
This commit is contained in:
Vasil Dimov 2023-12-06 13:55:06 +01:00
parent 033aa12a71
commit 0978d7d050
No known key found for this signature in database
GPG key ID: 54DF06F64B55CBBF
2 changed files with 84 additions and 29 deletions

View file

@ -253,6 +253,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "verifychain", 1, "nblocks" }, { "verifychain", 1, "nblocks" },
{ "getblockstats", 0, "hash_or_height" }, { "getblockstats", 0, "hash_or_height" },
{ "getblockstats", 1, "stats" }, { "getblockstats", 1, "stats" },
{ "getnetmsgstats", 0, "aggregate_by"},
{ "pruneblockchain", 0, "height" }, { "pruneblockchain", 0, "height" },
{ "keypoolrefill", 0, "newsize" }, { "keypoolrefill", 0, "newsize" },
{ "getrawmempool", 0, "verbose" }, { "getrawmempool", 0, "verbose" },

View file

@ -548,11 +548,44 @@ static RPCHelpMan getaddednodeinfo()
namespace net_stats { namespace net_stats {
namespace aggregate_dimensions {
static std::string DIRECTION{"direction"};
static std::string NETWORK{"network"};
static std::string CONNECTION_TYPE{"connection_type"};
static std::string MESSAGE_TYPE{"message_type"};
};
UniValue CreateJSON(const RPCHelpMan&, const JSONRPCRequest& request) UniValue CreateJSON(const RPCHelpMan&, const JSONRPCRequest& request)
{ {
const NodeContext& node = EnsureAnyNodeContext(request.context); const NodeContext& node = EnsureAnyNodeContext(request.context);
const CConnman& connman = EnsureConnman(node); const CConnman& connman = EnsureConnman(node);
// Used for a quick check if a string is in request.params[0] which is
// supposed to be a JSON array, e.g. ["direction", "network"].
std::unordered_set<std::string> aggregate_by;
if (request.params[0].isArray()) {
const UniValue& arr{request.params[0].get_array()};
for (size_t i = 0; i < arr.size(); ++i) {
const auto& agg{arr[i].get_str()};
if (agg != aggregate_dimensions::DIRECTION &&
agg != aggregate_dimensions::NETWORK &&
agg != aggregate_dimensions::CONNECTION_TYPE &&
agg != aggregate_dimensions::MESSAGE_TYPE) {
throw JSONRPCError(
RPC_INVALID_PARAMS,
strprintf(
R"(Unrecognized aggregation parameter: "%s". The array should consist of zero or more of "%s", "%s", "%s", "%s".)",
agg,
aggregate_dimensions::DIRECTION,
aggregate_dimensions::NETWORK,
aggregate_dimensions::CONNECTION_TYPE,
aggregate_dimensions::MESSAGE_TYPE));
}
aggregate_by.insert(agg);
}
}
// The keys might as well be an empty string (if aggregating by that dimension).
std::unordered_map< std::unordered_map<
std::string, // "sent" or "recv" std::string, // "sent" or "recv"
std::unordered_map<std::string, // "ipv4", "tor", ... std::unordered_map<std::string, // "ipv4", "tor", ...
@ -561,22 +594,27 @@ UniValue CreateJSON(const RPCHelpMan&, const JSONRPCRequest& request)
NetStats::BytesAndCount>>>> NetStats::BytesAndCount>>>>
result_map; result_map;
connman.GetNetStats().ForEach([&result_map](NetStats::Direction dir, connman.GetNetStats().ForEach([&aggregate_by, &result_map](NetStats::Direction dir,
Network net, Network net,
ConnectionType con, ConnectionType con,
const std::string& msg, const std::string& msg,
const NetStats::BytesAndCount& data) { const NetStats::BytesAndCount& data) {
const std::string dir_str{dir == NetStats::SENT ? "sent" : "recv"}; const std::string dir_str{aggregate_by.contains(aggregate_dimensions::DIRECTION) ? "" : dir == NetStats::SENT ? "sent" : "recv"};
const std::string net_str{GetNetworkName(net)}; const std::string net_str{aggregate_by.contains(aggregate_dimensions::NETWORK) ? "" : GetNetworkName(net)};
const std::string con_str{ConnectionTypeAsString(con)}; const std::string con_str{aggregate_by.contains(aggregate_dimensions::CONNECTION_TYPE) ? "" : ConnectionTypeAsString(con)};
const std::string msg_str{aggregate_by.contains(aggregate_dimensions::MESSAGE_TYPE) ? "" : msg};
result_map[dir_str][net_str][con_str][msg] += data; result_map[dir_str][net_str][con_str][msg_str] += data;
}); });
auto Add = [](UniValue& target, const std::string& key, const UniValue& val) { auto Add = [](UniValue& target, const std::string& key, const UniValue& val) {
if (val.empty()) { if (val.empty()) {
return; return;
} }
if (key.empty()) {
target = val;
return;
}
target.pushKV(key, val); target.pushKV(key, val);
}; };
@ -614,8 +652,29 @@ static RPCHelpMan getnetmsgstats()
{ {
return RPCHelpMan{ return RPCHelpMan{
"getnetmsgstats", "getnetmsgstats",
"\nReturns the messages count and total number of bytes for network traffic.\n", "\nReturns the messages count and total number of bytes for network traffic.\n"
{}, "Results may optionally be aggregated.\n",
{RPCArg{
"aggregate_by",
RPCArg::Type::ARR,
RPCArg::DefaultHint{"empty, no aggregation"},
"An array of keywords for aggregating the results.",
{RPCArg{"direction",
RPCArg::Type::STR,
RPCArg::Optional::OMITTED,
"Aggregate by direction and don't show direction in the result."},
RPCArg{"network",
RPCArg::Type::STR,
RPCArg::Optional::OMITTED,
"Aggregate by network and don't show network in the result."},
RPCArg{"connection_type",
RPCArg::Type::STR,
RPCArg::Optional::OMITTED,
"Aggregate by connection type and don't show connection type in the result."},
RPCArg{"message_type",
RPCArg::Type::STR,
RPCArg::Optional::OMITTED,
"Aggregate by message type and don't show message type in the result."}}}},
{RPCResult{ {RPCResult{
RPCResult::Type::OBJ_DYN, RPCResult::Type::OBJ_DYN,
"", "",
@ -638,29 +697,24 @@ static RPCHelpMan getnetmsgstats()
"inbound", "inbound",
true, true,
"The connection type over which the traffic occurred.", "The connection type over which the traffic occurred.",
{RPCResult{RPCResult::Type::OBJ, {RPCResult{
RPCResult::Type::OBJ,
"verack", "verack",
true, true,
"Type of the messages transferred.", "Type of the messages transferred.",
{RPCResult{RPCResult::Type::NUM, {
"bytes", RPCResult{RPCResult::Type::NUM, "bytes", false, "Total number of bytes.", {}, true},
false, RPCResult{RPCResult::Type::NUM, "count", false, "Total number of messages.", {}, true}
"Total number of bytes.", },
{},
true},
RPCResult{RPCResult::Type::NUM,
"count",
false,
"Total number of messages.",
{},
true}},
true}}, true}},
true}}, true}},
true}}, true}},
true}}, true}},
true}}, true}},
RPCExamples{HelpExampleCli("getnetmsgstats", "") + RPCExamples{HelpExampleCli("getnetmsgstats", "") +
HelpExampleRpc("getnetmsgstats", "")}, HelpExampleCli("getnetmsgstats", R"('["network", "message_type"]')") +
HelpExampleRpc("getnetmsgstats", "") +
HelpExampleRpc("getnetmsgstats", R"(["network", "message_type"])")},
net_stats::CreateJSON}; net_stats::CreateJSON};
} }