Browse Source

Merge pull request #5007

af82884 Add "warmup mode" for RPC server. (Daniel Kraft)
0.10
Wladimir J. van der Laan 10 years ago
parent
commit
06037f3f46
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 11
      doc/release-notes.md
  2. 85
      src/bitcoin-cli.cpp
  3. 14
      src/init.cpp
  4. 1
      src/rpcprotocol.h
  5. 24
      src/rpcserver.cpp
  6. 7
      src/rpcserver.h

11
doc/release-notes.md

@ -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).

85
src/bitcoin-cli.cpp

@ -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 {
// Parse reply try {
const Value& result = find_value(reply, "result"); const Object reply = CallRPC(strMethod, params);
const Value& error = find_value(reply, "error");
// Parse reply
if (error.type() != null_type) { const Value& result = find_value(reply, "result");
// Error const Value& error = find_value(reply, "error");
strPrint = "error: " + write_string(error, false);
int code = find_value(error.get_obj(), "code").get_int(); if (error.type() != null_type) {
nRet = abs(code); // Error
} else { const int code = find_value(error.get_obj(), "code").get_int();
// Result if (fWait && code == RPC_IN_WARMUP)
if (result.type() == null_type) throw CConnectionFailed("server in warmup");
strPrint = ""; strPrint = "error: " + write_string(error, false);
else if (result.type() == str_type) nRet = abs(code);
strPrint = result.get_str(); } else {
else // Result
strPrint = write_string(result, true); 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) { catch (boost::thread_interrupted) {
throw; throw;

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

1
src/rpcprotocol.h

@ -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,

24
src/rpcserver.cpp

@ -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

7
src/rpcserver.h

@ -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…
Cancel
Save