Merge #14982: rpc: Add getrpcinfo command
a0ac15459a
doc: Add getrpcinfo release notes (João Barbosa)251a91c1bf
qa: Add tests for getrpcinfo (João Barbosa)d0730f5ce4
rpc: Add getrpcinfo command (João Barbosa)068a8fc05f
rpc: Track active commands (João Barbosa)bf4383277d
rpc: Remove unused PreCommand signal (João Barbosa) Pull request description: The new `getrpcinfo` command exposes details of the RPC interface. The details can be configuration properties or runtime values/stats. This can be particular useful to coordinate concurrent functional tests (see #14958 from where this was extracted). Tree-SHA512: 7292cb6087f4c429973d991aa2b53ffa1327d5a213df7d6ba5fc69b01b2e1a411f6d1609fed9234896293317dab05f65064da48b8f2b4a998eba532591d31882
This commit is contained in:
commit
cf0c67b62c
3 changed files with 73 additions and 4 deletions
5
doc/release-notes-14982.md
Normal file
5
doc/release-notes-14982.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
New RPCs
|
||||
--------
|
||||
|
||||
- The RPC `getrpcinfo` returns runtime details of the RPC server. At the moment
|
||||
it returns the active commands and the corresponding execution time.
|
|
@ -31,11 +31,39 @@ static RPCTimerInterface* timerInterface = nullptr;
|
|||
/* Map of name to timer. */
|
||||
static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers;
|
||||
|
||||
struct RPCCommandExecutionInfo
|
||||
{
|
||||
std::string method;
|
||||
int64_t start;
|
||||
};
|
||||
|
||||
struct RPCServerInfo
|
||||
{
|
||||
Mutex mutex;
|
||||
std::list<RPCCommandExecutionInfo> active_commands GUARDED_BY(mutex);
|
||||
};
|
||||
|
||||
static RPCServerInfo g_rpc_server_info;
|
||||
|
||||
struct RPCCommandExecution
|
||||
{
|
||||
std::list<RPCCommandExecutionInfo>::iterator it;
|
||||
explicit RPCCommandExecution(const std::string& method)
|
||||
{
|
||||
LOCK(g_rpc_server_info.mutex);
|
||||
it = g_rpc_server_info.active_commands.insert(g_rpc_server_info.active_commands.cend(), {method, GetTimeMicros()});
|
||||
}
|
||||
~RPCCommandExecution()
|
||||
{
|
||||
LOCK(g_rpc_server_info.mutex);
|
||||
g_rpc_server_info.active_commands.erase(it);
|
||||
}
|
||||
};
|
||||
|
||||
static struct CRPCSignals
|
||||
{
|
||||
boost::signals2::signal<void ()> Started;
|
||||
boost::signals2::signal<void ()> Stopped;
|
||||
boost::signals2::signal<void (const CRPCCommand&)> PreCommand;
|
||||
} g_rpcSignals;
|
||||
|
||||
void RPCServer::OnStarted(std::function<void ()> slot)
|
||||
|
@ -254,11 +282,37 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest)
|
|||
return GetTime() - GetStartupTime();
|
||||
}
|
||||
|
||||
static UniValue getrpcinfo(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || request.params.size() > 0) {
|
||||
throw std::runtime_error(
|
||||
RPCHelpMan{"getrpcinfo",
|
||||
"\nReturns details of the RPC server.\n", {}}
|
||||
.ToString()
|
||||
);
|
||||
}
|
||||
|
||||
LOCK(g_rpc_server_info.mutex);
|
||||
UniValue active_commands(UniValue::VARR);
|
||||
for (const RPCCommandExecutionInfo& info : g_rpc_server_info.active_commands) {
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
entry.pushKV("method", info.method);
|
||||
entry.pushKV("duration", GetTimeMicros() - info.start);
|
||||
active_commands.push_back(entry);
|
||||
}
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.pushKV("active_commands", active_commands);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static const CRPCCommand vRPCCommands[] =
|
||||
{ // category name actor (function) argNames
|
||||
// --------------------- ------------------------ ----------------------- ----------
|
||||
/* Overall control/query calls */
|
||||
{ "control", "getrpcinfo", &getrpcinfo, {} },
|
||||
{ "control", "help", &help, {"command"} },
|
||||
{ "control", "stop", &stop, {"wait"} },
|
||||
{ "control", "uptime", &uptime, {} },
|
||||
|
@ -483,10 +537,9 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const
|
|||
if (!pcmd)
|
||||
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
|
||||
|
||||
g_rpcSignals.PreCommand(*pcmd);
|
||||
|
||||
try
|
||||
{
|
||||
RPCCommandExecution execution(request.strMethod);
|
||||
// Execute, convert arguments to array if necessary
|
||||
if (request.params.isObject()) {
|
||||
return pcmd->actor(transformNamedArguments(request, pcmd->argNames));
|
||||
|
|
|
@ -5,13 +5,23 @@
|
|||
"""Tests some generic aspects of the RPC interface."""
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal
|
||||
from test_framework.util import assert_equal, assert_greater_than_or_equal
|
||||
|
||||
class RPCInterfaceTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 1
|
||||
self.setup_clean_chain = True
|
||||
|
||||
def test_getrpcinfo(self):
|
||||
self.log.info("Testing getrpcinfo...")
|
||||
|
||||
info = self.nodes[0].getrpcinfo()
|
||||
assert_equal(len(info['active_commands']), 1)
|
||||
|
||||
command = info['active_commands'][0]
|
||||
assert_equal(command['method'], 'getrpcinfo')
|
||||
assert_greater_than_or_equal(command['duration'], 0)
|
||||
|
||||
def test_batch_request(self):
|
||||
self.log.info("Testing basic JSON-RPC batch request...")
|
||||
|
||||
|
@ -39,6 +49,7 @@ class RPCInterfaceTest(BitcoinTestFramework):
|
|||
assert result_by_id[3]['result'] is not None
|
||||
|
||||
def run_test(self):
|
||||
self.test_getrpcinfo()
|
||||
self.test_batch_request()
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue