mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-25 10:43:19 -03:00
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).
|
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
|
// 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[])
|
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);
|
SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL);
|
||||||
iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d);
|
iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d);
|
||||||
|
|
||||||
bool fWait = GetBoolArg("-rpcwait", false); // -rpcwait means try until server has started
|
const bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(BaseParams().RPCPort())));
|
||||||
do {
|
if (!fConnected)
|
||||||
bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(BaseParams().RPCPort())));
|
throw CConnectionFailed("couldn't connect to server");
|
||||||
if (fConnected) break;
|
|
||||||
if (fWait)
|
|
||||||
MilliSleep(1000);
|
|
||||||
else
|
|
||||||
throw runtime_error("couldn't connect to server");
|
|
||||||
} while (fWait);
|
|
||||||
|
|
||||||
// HTTP basic authentication
|
// HTTP basic authentication
|
||||||
string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
|
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]);
|
std::vector<std::string> strParams(&argv[2], &argv[argc]);
|
||||||
Array params = RPCConvertValues(strMethod, strParams);
|
Array params = RPCConvertValues(strMethod, strParams);
|
||||||
|
|
||||||
// Execute
|
// Execute and handle connection failures with -rpcwait
|
||||||
Object reply = CallRPC(strMethod, params);
|
const bool fWait = GetBoolArg("-rpcwait", false);
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
const Object reply = CallRPC(strMethod, params);
|
||||||
|
|
||||||
// Parse reply
|
// Parse reply
|
||||||
const Value& result = find_value(reply, "result");
|
const Value& result = find_value(reply, "result");
|
||||||
const Value& error = find_value(reply, "error");
|
const Value& error = find_value(reply, "error");
|
||||||
|
|
||||||
if (error.type() != null_type) {
|
if (error.type() != null_type) {
|
||||||
// Error
|
// Error
|
||||||
strPrint = "error: " + write_string(error, false);
|
const int code = find_value(error.get_obj(), "code").get_int();
|
||||||
int code = find_value(error.get_obj(), "code").get_int();
|
if (fWait && code == RPC_IN_WARMUP)
|
||||||
nRet = abs(code);
|
throw CConnectionFailed("server in warmup");
|
||||||
} else {
|
strPrint = "error: " + write_string(error, false);
|
||||||
// Result
|
nRet = abs(code);
|
||||||
if (result.type() == null_type)
|
} else {
|
||||||
strPrint = "";
|
// Result
|
||||||
else if (result.type() == str_type)
|
if (result.type() == null_type)
|
||||||
strPrint = result.get_str();
|
strPrint = "";
|
||||||
else
|
else if (result.type() == str_type)
|
||||||
strPrint = write_string(result, true);
|
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) {
|
catch (boost::thread_interrupted) {
|
||||||
throw;
|
throw;
|
||||||
|
|
14
src/init.cpp
14
src/init.cpp
|
@ -752,6 +752,17 @@ bool AppInit2(boost::thread_group& threadGroup)
|
||||||
threadGroup.create_thread(&ThreadScriptCheck);
|
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;
|
int64_t nStart;
|
||||||
|
|
||||||
// ********************************************************* Step 5: verify wallet database integrity
|
// ********************************************************* Step 5: verify wallet database integrity
|
||||||
|
@ -1248,8 +1259,6 @@ bool AppInit2(boost::thread_group& threadGroup)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
StartNode(threadGroup);
|
StartNode(threadGroup);
|
||||||
if (fServer)
|
|
||||||
StartRPCThreads();
|
|
||||||
|
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
// Generate coins in the background
|
// Generate coins in the background
|
||||||
|
@ -1259,6 +1268,7 @@ bool AppInit2(boost::thread_group& threadGroup)
|
||||||
|
|
||||||
// ********************************************************* Step 11: finished
|
// ********************************************************* Step 11: finished
|
||||||
|
|
||||||
|
SetRPCWarmupFinished();
|
||||||
uiInterface.InitMessage(_("Done loading"));
|
uiInterface.InitMessage(_("Done loading"));
|
||||||
|
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
|
|
|
@ -52,6 +52,7 @@ enum RPCErrorCode
|
||||||
RPC_VERIFY_ERROR = -25, // General error during transaction or block submission
|
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_REJECTED = -26, // Transaction or block was rejected by network rules
|
||||||
RPC_VERIFY_ALREADY_IN_CHAIN = -27, // Transaction already in chain
|
RPC_VERIFY_ALREADY_IN_CHAIN = -27, // Transaction already in chain
|
||||||
|
RPC_IN_WARMUP = -28, // Client still warming up
|
||||||
|
|
||||||
// Aliases for backward compatibility
|
// Aliases for backward compatibility
|
||||||
RPC_TRANSACTION_ERROR = RPC_VERIFY_ERROR,
|
RPC_TRANSACTION_ERROR = RPC_VERIFY_ERROR,
|
||||||
|
|
|
@ -34,6 +34,10 @@ using namespace std;
|
||||||
static std::string strRPCUserColonPass;
|
static std::string strRPCUserColonPass;
|
||||||
|
|
||||||
static bool fRPCRunning = false;
|
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
|
//! These are created by StartRPCThreads, destroyed in StopRPCThreads
|
||||||
static asio::io_service* rpc_io_service = NULL;
|
static asio::io_service* rpc_io_service = NULL;
|
||||||
static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers;
|
static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers;
|
||||||
|
@ -744,6 +748,19 @@ bool IsRPCRunning()
|
||||||
return fRPCRunning;
|
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)
|
void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func)
|
||||||
{
|
{
|
||||||
if (!err)
|
if (!err)
|
||||||
|
@ -870,6 +887,13 @@ static bool HTTPReq_JSONRPC(AcceptedConnection *conn,
|
||||||
if (!read_string(strRequest, valRequest))
|
if (!read_string(strRequest, valRequest))
|
||||||
throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
|
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;
|
string strReply;
|
||||||
|
|
||||||
// singleton request
|
// singleton request
|
||||||
|
|
|
@ -45,6 +45,13 @@ void StopRPCThreads();
|
||||||
/* Query whether RPC is running */
|
/* Query whether RPC is running */
|
||||||
bool IsRPCRunning();
|
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
|
* 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.
|
* the right number of arguments are passed, just that any passed are the correct type.
|
||||||
|
|
Loading…
Add table
Reference in a new issue