Merge pull request #5007
af82884
Add "warmup mode" for RPC server. (Daniel Kraft)
This commit is contained in:
commit
06037f3f46
6 changed files with 108 additions and 30 deletions
|
@ -84,3 +84,14 @@ Using wildcards will result in the rule being rejected with the following error
|
|||
|
||||
Error: Invalid -rpcallowip subnet specification: *. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).
|
||||
|
||||
RPC Server "Warm-Up" Mode
|
||||
=========================
|
||||
|
||||
The RPC server is started earlier now, before most of the expensive
|
||||
intialisations like loading the block index. It is available now almost
|
||||
immediately after starting the process. However, until all initialisations
|
||||
are done, it always returns an immediate error with code -28 to all calls.
|
||||
|
||||
This new behaviour can be useful for clients to know that a server is already
|
||||
started and will be available soon (for instance, so that they do not
|
||||
have to start it themselves).
|
||||
|
|
|
@ -46,6 +46,21 @@ std::string HelpMessageCli()
|
|||
//
|
||||
// Start
|
||||
//
|
||||
|
||||
//
|
||||
// Exception thrown on connection error. This error is used to determine
|
||||
// when to wait if -rpcwait is given.
|
||||
//
|
||||
class CConnectionFailed : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
|
||||
explicit inline CConnectionFailed(const std::string& msg) :
|
||||
std::runtime_error(msg)
|
||||
{}
|
||||
|
||||
};
|
||||
|
||||
static bool AppInitRPC(int argc, char* argv[])
|
||||
{
|
||||
//
|
||||
|
@ -101,15 +116,9 @@ Object CallRPC(const string& strMethod, const Array& params)
|
|||
SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL);
|
||||
iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d);
|
||||
|
||||
bool fWait = GetBoolArg("-rpcwait", false); // -rpcwait means try until server has started
|
||||
do {
|
||||
bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(BaseParams().RPCPort())));
|
||||
if (fConnected) break;
|
||||
if (fWait)
|
||||
MilliSleep(1000);
|
||||
else
|
||||
throw runtime_error("couldn't connect to server");
|
||||
} while (fWait);
|
||||
const bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(BaseParams().RPCPort())));
|
||||
if (!fConnected)
|
||||
throw CConnectionFailed("couldn't connect to server");
|
||||
|
||||
// HTTP basic authentication
|
||||
string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
|
||||
|
@ -168,27 +177,43 @@ int CommandLineRPC(int argc, char *argv[])
|
|||
std::vector<std::string> strParams(&argv[2], &argv[argc]);
|
||||
Array params = RPCConvertValues(strMethod, strParams);
|
||||
|
||||
// Execute
|
||||
Object reply = CallRPC(strMethod, params);
|
||||
// Execute and handle connection failures with -rpcwait
|
||||
const bool fWait = GetBoolArg("-rpcwait", false);
|
||||
do {
|
||||
try {
|
||||
const Object reply = CallRPC(strMethod, params);
|
||||
|
||||
// Parse reply
|
||||
const Value& result = find_value(reply, "result");
|
||||
const Value& error = find_value(reply, "error");
|
||||
// Parse reply
|
||||
const Value& result = find_value(reply, "result");
|
||||
const Value& error = find_value(reply, "error");
|
||||
|
||||
if (error.type() != null_type) {
|
||||
// Error
|
||||
strPrint = "error: " + write_string(error, false);
|
||||
int code = find_value(error.get_obj(), "code").get_int();
|
||||
nRet = abs(code);
|
||||
} else {
|
||||
// Result
|
||||
if (result.type() == null_type)
|
||||
strPrint = "";
|
||||
else if (result.type() == str_type)
|
||||
strPrint = result.get_str();
|
||||
else
|
||||
strPrint = write_string(result, true);
|
||||
}
|
||||
if (error.type() != null_type) {
|
||||
// Error
|
||||
const int code = find_value(error.get_obj(), "code").get_int();
|
||||
if (fWait && code == RPC_IN_WARMUP)
|
||||
throw CConnectionFailed("server in warmup");
|
||||
strPrint = "error: " + write_string(error, false);
|
||||
nRet = abs(code);
|
||||
} else {
|
||||
// Result
|
||||
if (result.type() == null_type)
|
||||
strPrint = "";
|
||||
else if (result.type() == str_type)
|
||||
strPrint = result.get_str();
|
||||
else
|
||||
strPrint = write_string(result, true);
|
||||
}
|
||||
|
||||
// Connection succeeded, no need to retry.
|
||||
break;
|
||||
}
|
||||
catch (const CConnectionFailed& e) {
|
||||
if (fWait)
|
||||
MilliSleep(1000);
|
||||
else
|
||||
throw;
|
||||
}
|
||||
} while (fWait);
|
||||
}
|
||||
catch (boost::thread_interrupted) {
|
||||
throw;
|
||||
|
|
14
src/init.cpp
14
src/init.cpp
|
@ -752,6 +752,17 @@ bool AppInit2(boost::thread_group& threadGroup)
|
|||
threadGroup.create_thread(&ThreadScriptCheck);
|
||||
}
|
||||
|
||||
/* Start the RPC server already. It will be started in "warmup" mode
|
||||
* and not really process calls already (but it will signify connections
|
||||
* that the server is there and will be ready later). Warmup mode will
|
||||
* be disabled when initialisation is finished.
|
||||
*/
|
||||
if (fServer)
|
||||
{
|
||||
uiInterface.InitMessage.connect(SetRPCWarmupStatus);
|
||||
StartRPCThreads();
|
||||
}
|
||||
|
||||
int64_t nStart;
|
||||
|
||||
// ********************************************************* Step 5: verify wallet database integrity
|
||||
|
@ -1248,8 +1259,6 @@ bool AppInit2(boost::thread_group& threadGroup)
|
|||
#endif
|
||||
|
||||
StartNode(threadGroup);
|
||||
if (fServer)
|
||||
StartRPCThreads();
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
// Generate coins in the background
|
||||
|
@ -1259,6 +1268,7 @@ bool AppInit2(boost::thread_group& threadGroup)
|
|||
|
||||
// ********************************************************* Step 11: finished
|
||||
|
||||
SetRPCWarmupFinished();
|
||||
uiInterface.InitMessage(_("Done loading"));
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
|
|
|
@ -52,6 +52,7 @@ enum RPCErrorCode
|
|||
RPC_VERIFY_ERROR = -25, // General error during transaction or block submission
|
||||
RPC_VERIFY_REJECTED = -26, // Transaction or block was rejected by network rules
|
||||
RPC_VERIFY_ALREADY_IN_CHAIN = -27, // Transaction already in chain
|
||||
RPC_IN_WARMUP = -28, // Client still warming up
|
||||
|
||||
// Aliases for backward compatibility
|
||||
RPC_TRANSACTION_ERROR = RPC_VERIFY_ERROR,
|
||||
|
|
|
@ -34,6 +34,10 @@ using namespace std;
|
|||
static std::string strRPCUserColonPass;
|
||||
|
||||
static bool fRPCRunning = false;
|
||||
static bool fRPCInWarmup = true;
|
||||
static std::string rpcWarmupStatus("RPC server started");
|
||||
static CCriticalSection cs_rpcWarmup;
|
||||
|
||||
//! These are created by StartRPCThreads, destroyed in StopRPCThreads
|
||||
static asio::io_service* rpc_io_service = NULL;
|
||||
static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers;
|
||||
|
@ -744,6 +748,19 @@ bool IsRPCRunning()
|
|||
return fRPCRunning;
|
||||
}
|
||||
|
||||
void SetRPCWarmupStatus(const std::string& newStatus)
|
||||
{
|
||||
LOCK(cs_rpcWarmup);
|
||||
rpcWarmupStatus = newStatus;
|
||||
}
|
||||
|
||||
void SetRPCWarmupFinished()
|
||||
{
|
||||
LOCK(cs_rpcWarmup);
|
||||
assert(fRPCInWarmup);
|
||||
fRPCInWarmup = false;
|
||||
}
|
||||
|
||||
void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func)
|
||||
{
|
||||
if (!err)
|
||||
|
@ -870,6 +887,13 @@ static bool HTTPReq_JSONRPC(AcceptedConnection *conn,
|
|||
if (!read_string(strRequest, valRequest))
|
||||
throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
|
||||
|
||||
// Return immediately if in warmup
|
||||
{
|
||||
LOCK(cs_rpcWarmup);
|
||||
if (fRPCInWarmup)
|
||||
throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
|
||||
}
|
||||
|
||||
string strReply;
|
||||
|
||||
// singleton request
|
||||
|
|
|
@ -45,6 +45,13 @@ void StopRPCThreads();
|
|||
/* Query whether RPC is running */
|
||||
bool IsRPCRunning();
|
||||
|
||||
/* Set the RPC warmup status. When this is done, all RPC calls will error out
|
||||
* immediately with RPC_IN_WARMUP.
|
||||
*/
|
||||
void SetRPCWarmupStatus(const std::string& newStatus);
|
||||
/* Mark warmup as done. RPC calls will be processed from now on. */
|
||||
void SetRPCWarmupFinished();
|
||||
|
||||
/**
|
||||
* Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
|
||||
* the right number of arguments are passed, just that any passed are the correct type.
|
||||
|
|
Loading…
Reference in a new issue