mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Reorganize source file hierarchy:
* Rename unity files * Move some modules to new subdirectories * Remove obsolete Visual Studio project files * Remove obsolete coding style and TODO list
This commit is contained in:
787
src/ripple/module/core/functional/Config.cpp
Normal file
787
src/ripple/module/core/functional/Config.cpp
Normal file
@@ -0,0 +1,787 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <beast/module/core/text/LexicalCast.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
//
|
||||
// TODO: Check permissions on config file before using it.
|
||||
//
|
||||
|
||||
// Fees are in XRP.
|
||||
#define DEFAULT_FEE_DEFAULT 10
|
||||
#define DEFAULT_FEE_ACCOUNT_RESERVE 200*SYSTEM_CURRENCY_PARTS
|
||||
#define DEFAULT_FEE_OWNER_RESERVE 50*SYSTEM_CURRENCY_PARTS
|
||||
#define DEFAULT_FEE_NICKNAME_CREATE 1000
|
||||
#define DEFAULT_FEE_OFFER DEFAULT_FEE_DEFAULT
|
||||
#define DEFAULT_FEE_OPERATION 1
|
||||
|
||||
/** Parses a set of strings into IP::Endpoint
|
||||
Strings which fail to parse are not included in the output. If a stream is
|
||||
provided, human readable diagnostic error messages are written for each
|
||||
failed parse.
|
||||
@param out An OutputSequence to store the IP::Endpoint list
|
||||
@param first The begining of the string input sequence
|
||||
@param last The one-past-the-end of the string input sequence
|
||||
*/
|
||||
template <class OutputSequence, class InputIterator>
|
||||
void parseAddresses (OutputSequence& out, InputIterator first, InputIterator last,
|
||||
beast::Journal::Stream stream = beast::Journal::Stream ())
|
||||
{
|
||||
while (first != last)
|
||||
{
|
||||
auto const str (*first);
|
||||
++first;
|
||||
{
|
||||
beast::IP::Endpoint const addr (beast::IP::Endpoint::from_string (str));
|
||||
if (! is_unspecified (addr))
|
||||
{
|
||||
out.push_back (addr);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
{
|
||||
beast::IP::Endpoint const addr (beast::IP::Endpoint::from_string_altform (str));
|
||||
if (! is_unspecified (addr))
|
||||
{
|
||||
out.push_back (addr);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (stream) stream <<
|
||||
"Config: \"" << str << "\" is not a valid IP address.";
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Config::Config ()
|
||||
: m_rpcPort (5001)
|
||||
{
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// VFALCO NOTE Clean member area
|
||||
//
|
||||
|
||||
peerListeningPort = SYSTEM_PEER_PORT;
|
||||
|
||||
peerPROXYListeningPort = 0;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Defaults
|
||||
//
|
||||
|
||||
NETWORK_START_TIME = 1319844908;
|
||||
|
||||
RPC_SECURE = 0;
|
||||
WEBSOCKET_PORT = SYSTEM_WEBSOCKET_PORT;
|
||||
WEBSOCKET_PUBLIC_PORT = SYSTEM_WEBSOCKET_PUBLIC_PORT;
|
||||
WEBSOCKET_PUBLIC_SECURE = 1;
|
||||
WEBSOCKET_PROXY_PORT = 0;
|
||||
WEBSOCKET_PROXY_SECURE = 1;
|
||||
WEBSOCKET_SECURE = 0;
|
||||
WEBSOCKET_PING_FREQ = (5 * 60);
|
||||
NUMBER_CONNECTIONS = 30;
|
||||
|
||||
// a new ledger every minute
|
||||
LEDGER_SECONDS = 60;
|
||||
LEDGER_CREATOR = false;
|
||||
|
||||
RPC_ALLOW_REMOTE = false;
|
||||
RPC_ADMIN_ALLOW.push_back (beast::IP::Endpoint::from_string("127.0.0.1"));
|
||||
|
||||
PEER_SSL_CIPHER_LIST = DEFAULT_PEER_SSL_CIPHER_LIST;
|
||||
PEER_SCAN_INTERVAL_MIN = DEFAULT_PEER_SCAN_INTERVAL_MIN;
|
||||
|
||||
PEER_START_MAX = DEFAULT_PEER_START_MAX;
|
||||
PEER_CONNECT_LOW_WATER = DEFAULT_PEER_CONNECT_LOW_WATER;
|
||||
|
||||
PEER_PRIVATE = false;
|
||||
PEERS_MAX = 0; // indicates "use default"
|
||||
|
||||
TRANSACTION_FEE_BASE = DEFAULT_FEE_DEFAULT;
|
||||
|
||||
NETWORK_QUORUM = 0; // Don't need to see other nodes
|
||||
VALIDATION_QUORUM = 1; // Only need one node to vouch
|
||||
|
||||
FEE_ACCOUNT_RESERVE = DEFAULT_FEE_ACCOUNT_RESERVE;
|
||||
FEE_OWNER_RESERVE = DEFAULT_FEE_OWNER_RESERVE;
|
||||
FEE_NICKNAME_CREATE = DEFAULT_FEE_NICKNAME_CREATE;
|
||||
FEE_OFFER = DEFAULT_FEE_OFFER;
|
||||
FEE_DEFAULT = DEFAULT_FEE_DEFAULT;
|
||||
FEE_CONTRACT_OPERATION = DEFAULT_FEE_OPERATION;
|
||||
|
||||
LEDGER_HISTORY = 256;
|
||||
FETCH_DEPTH = 1000000000;
|
||||
|
||||
PATH_SEARCH_OLD = DEFAULT_PATH_SEARCH_OLD;
|
||||
PATH_SEARCH = DEFAULT_PATH_SEARCH;
|
||||
PATH_SEARCH_FAST = DEFAULT_PATH_SEARCH_FAST;
|
||||
PATH_SEARCH_MAX = DEFAULT_PATH_SEARCH_MAX;
|
||||
|
||||
ACCOUNT_PROBE_MAX = 10;
|
||||
|
||||
VALIDATORS_SITE = "";
|
||||
|
||||
SSL_VERIFY = true;
|
||||
|
||||
ELB_SUPPORT = false;
|
||||
RUN_STANDALONE = false;
|
||||
doImport = false;
|
||||
START_UP = NORMAL;
|
||||
}
|
||||
|
||||
void Config::setup (const std::string& strConf, bool bQuiet)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
std::string strDbPath, strConfFile;
|
||||
|
||||
//
|
||||
// Determine the config and data directories.
|
||||
// If the config file is found in the current working directory, use the current working directory as the config directory and
|
||||
// that with "db" as the data directory.
|
||||
//
|
||||
|
||||
QUIET = bQuiet;
|
||||
NODE_SIZE = 0;
|
||||
|
||||
strDbPath = Helpers::getDatabaseDirName ();
|
||||
strConfFile = strConf.empty () ? Helpers::getConfigFileName () : strConf;
|
||||
|
||||
VALIDATORS_BASE = Helpers::getValidatorsFileName ();
|
||||
|
||||
VALIDATORS_URI = boost::str (boost::format ("/%s") % VALIDATORS_BASE);
|
||||
|
||||
SIGN_TRANSACTION = HashPrefix::txSign;
|
||||
SIGN_VALIDATION = HashPrefix::validation;
|
||||
SIGN_PROPOSAL = HashPrefix::proposal;
|
||||
|
||||
if (!strConf.empty ())
|
||||
{
|
||||
// --conf=<path> : everything is relative that file.
|
||||
CONFIG_FILE = strConfFile;
|
||||
CONFIG_DIR = boost::filesystem::absolute (CONFIG_FILE);
|
||||
CONFIG_DIR.remove_filename ();
|
||||
DATA_DIR = CONFIG_DIR / strDbPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONFIG_DIR = boost::filesystem::current_path ();
|
||||
CONFIG_FILE = CONFIG_DIR / strConfFile;
|
||||
DATA_DIR = CONFIG_DIR / strDbPath;
|
||||
|
||||
if (exists (CONFIG_FILE)
|
||||
// Can we figure out XDG dirs?
|
||||
|| (!getenv ("HOME") && (!getenv ("XDG_CONFIG_HOME") || !getenv ("XDG_DATA_HOME"))))
|
||||
{
|
||||
// Current working directory is fine, put dbs in a subdir.
|
||||
nothing ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Construct XDG config and data home.
|
||||
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
std::string strHome = strGetEnv ("HOME");
|
||||
std::string strXdgConfigHome = strGetEnv ("XDG_CONFIG_HOME");
|
||||
std::string strXdgDataHome = strGetEnv ("XDG_DATA_HOME");
|
||||
|
||||
if (strXdgConfigHome.empty ())
|
||||
{
|
||||
// $XDG_CONFIG_HOME was not set, use default based on $HOME.
|
||||
strXdgConfigHome = boost::str (boost::format ("%s/.config") % strHome);
|
||||
}
|
||||
|
||||
if (strXdgDataHome.empty ())
|
||||
{
|
||||
// $XDG_DATA_HOME was not set, use default based on $HOME.
|
||||
strXdgDataHome = boost::str (boost::format ("%s/.local/share") % strHome);
|
||||
}
|
||||
|
||||
CONFIG_DIR = boost::str (boost::format ("%s/" SYSTEM_NAME) % strXdgConfigHome);
|
||||
CONFIG_FILE = CONFIG_DIR / strConfFile;
|
||||
DATA_DIR = boost::str (boost::format ("%s/" SYSTEM_NAME) % strXdgDataHome);
|
||||
|
||||
boost::filesystem::create_directories (CONFIG_DIR, ec);
|
||||
|
||||
if (ec)
|
||||
throw std::runtime_error (boost::str (boost::format ("Can not create %s") % CONFIG_DIR));
|
||||
}
|
||||
}
|
||||
|
||||
HTTPClient::initializeSSLContext();
|
||||
|
||||
// Update default values
|
||||
load ();
|
||||
|
||||
// Log::out() << "CONFIG FILE: " << CONFIG_FILE;
|
||||
// Log::out() << "CONFIG DIR: " << CONFIG_DIR;
|
||||
// Log::out() << "DATA DIR: " << DATA_DIR;
|
||||
|
||||
boost::filesystem::create_directories (DATA_DIR, ec);
|
||||
|
||||
if (ec)
|
||||
throw std::runtime_error (boost::str (boost::format ("Can not create %s") % DATA_DIR));
|
||||
|
||||
// Create the new unified database
|
||||
m_moduleDbPath = getDatabaseDir();
|
||||
|
||||
// This code is temporarily disabled, and modules will fall back to using
|
||||
// per-module databases (e.g. "peerfinder.sqlite") under the module db path
|
||||
//
|
||||
//if (m_moduleDbPath.isDirectory ())
|
||||
// m_moduleDbPath = m_moduleDbPath.getChildFile("rippled.sqlite");
|
||||
}
|
||||
|
||||
void Config::load ()
|
||||
{
|
||||
if (!QUIET)
|
||||
Log::out() << "Loading: " << CONFIG_FILE;
|
||||
|
||||
std::ifstream ifsConfig (CONFIG_FILE.c_str (), std::ios::in);
|
||||
|
||||
if (!ifsConfig)
|
||||
{
|
||||
Log::out() << "Failed to open '" << CONFIG_FILE << "'.";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string strConfigFile;
|
||||
|
||||
strConfigFile.assign ((std::istreambuf_iterator<char> (ifsConfig)),
|
||||
std::istreambuf_iterator<char> ());
|
||||
|
||||
if (ifsConfig.bad ())
|
||||
{
|
||||
Log::out() << "Failed to read '" << CONFIG_FILE << "'.";
|
||||
}
|
||||
else
|
||||
{
|
||||
Section secConfig = ParseSection (strConfigFile, true);
|
||||
std::string strTemp;
|
||||
|
||||
// XXX Leak
|
||||
Section::mapped_type* smtTmp;
|
||||
|
||||
smtTmp = SectionEntries (secConfig, SECTION_VALIDATORS);
|
||||
|
||||
if (smtTmp)
|
||||
{
|
||||
validators = *smtTmp;
|
||||
}
|
||||
|
||||
smtTmp = SectionEntries (secConfig, SECTION_CLUSTER_NODES);
|
||||
|
||||
if (smtTmp)
|
||||
{
|
||||
CLUSTER_NODES = *smtTmp;
|
||||
}
|
||||
|
||||
smtTmp = SectionEntries (secConfig, SECTION_IPS);
|
||||
|
||||
if (smtTmp)
|
||||
{
|
||||
IPS = *smtTmp;
|
||||
}
|
||||
|
||||
smtTmp = SectionEntries (secConfig, SECTION_IPS_FIXED);
|
||||
|
||||
if (smtTmp)
|
||||
{
|
||||
IPS_FIXED = *smtTmp;
|
||||
}
|
||||
|
||||
smtTmp = SectionEntries (secConfig, SECTION_SNTP);
|
||||
|
||||
if (smtTmp)
|
||||
{
|
||||
SNTP_SERVERS = *smtTmp;
|
||||
}
|
||||
|
||||
smtTmp = SectionEntries (secConfig, SECTION_RPC_STARTUP);
|
||||
|
||||
if (smtTmp)
|
||||
{
|
||||
RPC_STARTUP = Json::arrayValue;
|
||||
|
||||
BOOST_FOREACH (const std::string & strJson, *smtTmp)
|
||||
{
|
||||
Json::Reader jrReader;
|
||||
Json::Value jvCommand;
|
||||
|
||||
if (!jrReader.parse (strJson, jvCommand))
|
||||
throw std::runtime_error (boost::str (boost::format ("Couldn't parse [" SECTION_RPC_STARTUP "] command: %s") % strJson));
|
||||
|
||||
RPC_STARTUP.append (jvCommand);
|
||||
}
|
||||
}
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_DATABASE_PATH, DATABASE_PATH))
|
||||
DATA_DIR = DATABASE_PATH;
|
||||
|
||||
|
||||
(void) SectionSingleB (secConfig, SECTION_VALIDATORS_SITE, VALIDATORS_SITE);
|
||||
|
||||
(void) SectionSingleB (secConfig, SECTION_PEER_IP, PEER_IP);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_PEER_PRIVATE, strTemp))
|
||||
PEER_PRIVATE = beast::lexicalCastThrow <bool> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_PEERS_MAX, strTemp))
|
||||
PEERS_MAX = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
smtTmp = SectionEntries (secConfig, SECTION_RPC_ADMIN_ALLOW);
|
||||
|
||||
if (smtTmp)
|
||||
{
|
||||
std::vector<beast::IP::Endpoint> parsedAddresses;
|
||||
//parseAddresses<std::vector<beast::IP::Endpoint>, std::vector<std::string>::const_iterator>
|
||||
// (parsedAddresses, (*smtTmp).cbegin(), (*smtTmp).cend());
|
||||
parseAddresses (parsedAddresses, (*smtTmp).cbegin(), (*smtTmp).cend());
|
||||
RPC_ADMIN_ALLOW.insert (RPC_ADMIN_ALLOW.end(),
|
||||
parsedAddresses.cbegin (), parsedAddresses.cend ());
|
||||
}
|
||||
|
||||
(void) SectionSingleB (secConfig, SECTION_RPC_ADMIN_PASSWORD, RPC_ADMIN_PASSWORD);
|
||||
(void) SectionSingleB (secConfig, SECTION_RPC_ADMIN_USER, RPC_ADMIN_USER);
|
||||
(void) SectionSingleB (secConfig, SECTION_RPC_IP, m_rpcIP);
|
||||
(void) SectionSingleB (secConfig, SECTION_RPC_PASSWORD, RPC_PASSWORD);
|
||||
(void) SectionSingleB (secConfig, SECTION_RPC_USER, RPC_USER);
|
||||
|
||||
insightSettings = parseKeyValueSection (secConfig, SECTION_INSIGHT);
|
||||
|
||||
//---------------------------------------
|
||||
//
|
||||
// VFALCO BEGIN CLEAN
|
||||
//
|
||||
nodeDatabase = parseKeyValueSection (
|
||||
secConfig, ConfigSection::nodeDatabase ());
|
||||
|
||||
ephemeralNodeDatabase = parseKeyValueSection (
|
||||
secConfig, ConfigSection::tempNodeDatabase ());
|
||||
|
||||
importNodeDatabase = parseKeyValueSection (
|
||||
secConfig, ConfigSection::importNodeDatabase ());
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_PEER_PORT, strTemp))
|
||||
peerListeningPort = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_PEER_PROXY_PORT, strTemp))
|
||||
{
|
||||
peerPROXYListeningPort = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
if (peerPROXYListeningPort != 0 && peerPROXYListeningPort == peerListeningPort)
|
||||
throw std::runtime_error ("Peer and proxy listening ports can't be the same.");
|
||||
}
|
||||
else
|
||||
{
|
||||
peerPROXYListeningPort = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// VFALCO END CLEAN
|
||||
//
|
||||
//---------------------------------------
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_RPC_PORT, strTemp))
|
||||
m_rpcPort = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, "ledger_creator" , strTemp))
|
||||
LEDGER_CREATOR = beast::lexicalCastThrow <bool> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_RPC_ALLOW_REMOTE, strTemp))
|
||||
RPC_ALLOW_REMOTE = beast::lexicalCastThrow <bool> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_NODE_SIZE, strTemp))
|
||||
{
|
||||
if (strTemp == "tiny")
|
||||
NODE_SIZE = 0;
|
||||
else if (strTemp == "small")
|
||||
NODE_SIZE = 1;
|
||||
else if (strTemp == "medium")
|
||||
NODE_SIZE = 2;
|
||||
else if (strTemp == "large")
|
||||
NODE_SIZE = 3;
|
||||
else if (strTemp == "huge")
|
||||
NODE_SIZE = 4;
|
||||
else
|
||||
{
|
||||
NODE_SIZE = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
if (NODE_SIZE < 0)
|
||||
NODE_SIZE = 0;
|
||||
else if (NODE_SIZE > 4)
|
||||
NODE_SIZE = 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_ELB_SUPPORT, strTemp))
|
||||
ELB_SUPPORT = beast::lexicalCastThrow <bool> (strTemp);
|
||||
|
||||
(void) SectionSingleB (secConfig, SECTION_WEBSOCKET_IP, WEBSOCKET_IP);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PORT, strTemp))
|
||||
WEBSOCKET_PORT = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
(void) SectionSingleB (secConfig, SECTION_WEBSOCKET_PUBLIC_IP, WEBSOCKET_PUBLIC_IP);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PUBLIC_PORT, strTemp))
|
||||
WEBSOCKET_PUBLIC_PORT = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
(void) SectionSingleB (secConfig, SECTION_WEBSOCKET_PROXY_IP, WEBSOCKET_PROXY_IP);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PROXY_PORT, strTemp))
|
||||
WEBSOCKET_PROXY_PORT = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_WEBSOCKET_SECURE, strTemp))
|
||||
WEBSOCKET_SECURE = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PUBLIC_SECURE, strTemp))
|
||||
WEBSOCKET_PUBLIC_SECURE = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PROXY_SECURE, strTemp))
|
||||
WEBSOCKET_PROXY_SECURE = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_WEBSOCKET_PING_FREQ, strTemp))
|
||||
WEBSOCKET_PING_FREQ = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
SectionSingleB (secConfig, SECTION_WEBSOCKET_SSL_CERT, WEBSOCKET_SSL_CERT);
|
||||
SectionSingleB (secConfig, SECTION_WEBSOCKET_SSL_CHAIN, WEBSOCKET_SSL_CHAIN);
|
||||
SectionSingleB (secConfig, SECTION_WEBSOCKET_SSL_KEY, WEBSOCKET_SSL_KEY);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_RPC_SECURE, strTemp))
|
||||
RPC_SECURE = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
SectionSingleB (secConfig, SECTION_RPC_SSL_CERT, RPC_SSL_CERT);
|
||||
SectionSingleB (secConfig, SECTION_RPC_SSL_CHAIN, RPC_SSL_CHAIN);
|
||||
SectionSingleB (secConfig, SECTION_RPC_SSL_KEY, RPC_SSL_KEY);
|
||||
|
||||
|
||||
SectionSingleB (secConfig, SECTION_SSL_VERIFY_FILE, SSL_VERIFY_FILE);
|
||||
SectionSingleB (secConfig, SECTION_SSL_VERIFY_DIR, SSL_VERIFY_DIR);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_SSL_VERIFY, strTemp))
|
||||
SSL_VERIFY = beast::lexicalCastThrow <bool> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_VALIDATION_SEED, strTemp))
|
||||
{
|
||||
VALIDATION_SEED.setSeedGeneric (strTemp);
|
||||
|
||||
if (VALIDATION_SEED.isValid ())
|
||||
{
|
||||
VALIDATION_PUB = RippleAddress::createNodePublic (VALIDATION_SEED);
|
||||
VALIDATION_PRIV = RippleAddress::createNodePrivate (VALIDATION_SEED);
|
||||
}
|
||||
}
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_NODE_SEED, strTemp))
|
||||
{
|
||||
NODE_SEED.setSeedGeneric (strTemp);
|
||||
|
||||
if (NODE_SEED.isValid ())
|
||||
{
|
||||
NODE_PUB = RippleAddress::createNodePublic (NODE_SEED);
|
||||
NODE_PRIV = RippleAddress::createNodePrivate (NODE_SEED);
|
||||
}
|
||||
}
|
||||
|
||||
(void) SectionSingleB (secConfig, SECTION_PEER_SSL_CIPHER_LIST, PEER_SSL_CIPHER_LIST);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_PEER_SCAN_INTERVAL_MIN, strTemp))
|
||||
// Minimum for min is 60 seconds.
|
||||
PEER_SCAN_INTERVAL_MIN = std::max (60, beast::lexicalCastThrow <int> (strTemp));
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_PEER_START_MAX, strTemp))
|
||||
PEER_START_MAX = std::max (1, beast::lexicalCastThrow <int> (strTemp));
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_PEER_CONNECT_LOW_WATER, strTemp))
|
||||
PEER_CONNECT_LOW_WATER = std::max (1, beast::lexicalCastThrow <int> (strTemp));
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_NETWORK_QUORUM, strTemp))
|
||||
NETWORK_QUORUM = std::max (0, beast::lexicalCastThrow <int> (strTemp));
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_VALIDATION_QUORUM, strTemp))
|
||||
VALIDATION_QUORUM = std::max (0, beast::lexicalCastThrow <int> (strTemp));
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_FEE_ACCOUNT_RESERVE, strTemp))
|
||||
FEE_ACCOUNT_RESERVE = beast::lexicalCastThrow <std::uint64_t> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_FEE_OWNER_RESERVE, strTemp))
|
||||
FEE_OWNER_RESERVE = beast::lexicalCastThrow <std::uint64_t> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_FEE_NICKNAME_CREATE, strTemp))
|
||||
FEE_NICKNAME_CREATE = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_FEE_OFFER, strTemp))
|
||||
FEE_OFFER = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_FEE_DEFAULT, strTemp))
|
||||
FEE_DEFAULT = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_FEE_OPERATION, strTemp))
|
||||
FEE_CONTRACT_OPERATION = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_LEDGER_HISTORY, strTemp))
|
||||
{
|
||||
boost::to_lower (strTemp);
|
||||
|
||||
if (strTemp == "full")
|
||||
LEDGER_HISTORY = 1000000000u;
|
||||
else if (strTemp == "none")
|
||||
LEDGER_HISTORY = 0;
|
||||
else
|
||||
LEDGER_HISTORY = beast::lexicalCastThrow <std::uint32_t> (strTemp);
|
||||
}
|
||||
if (SectionSingleB (secConfig, SECTION_FETCH_DEPTH, strTemp))
|
||||
{
|
||||
boost::to_lower (strTemp);
|
||||
|
||||
if (strTemp == "none")
|
||||
FETCH_DEPTH = 0;
|
||||
else if (strTemp == "full")
|
||||
FETCH_DEPTH = 1000000000u;
|
||||
else
|
||||
FETCH_DEPTH = beast::lexicalCastThrow <std::uint32_t> (strTemp);
|
||||
|
||||
if (FETCH_DEPTH < 10)
|
||||
FETCH_DEPTH = 10;
|
||||
}
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_PATH_SEARCH_OLD, strTemp))
|
||||
PATH_SEARCH_OLD = beast::lexicalCastThrow <int> (strTemp);
|
||||
if (SectionSingleB (secConfig, SECTION_PATH_SEARCH, strTemp))
|
||||
PATH_SEARCH = beast::lexicalCastThrow <int> (strTemp);
|
||||
if (SectionSingleB (secConfig, SECTION_PATH_SEARCH_FAST, strTemp))
|
||||
PATH_SEARCH_FAST = beast::lexicalCastThrow <int> (strTemp);
|
||||
if (SectionSingleB (secConfig, SECTION_PATH_SEARCH_MAX, strTemp))
|
||||
PATH_SEARCH_MAX = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_ACCOUNT_PROBE_MAX, strTemp))
|
||||
ACCOUNT_PROBE_MAX = beast::lexicalCastThrow <int> (strTemp);
|
||||
|
||||
(void) SectionSingleB (secConfig, SECTION_SMS_FROM, SMS_FROM);
|
||||
(void) SectionSingleB (secConfig, SECTION_SMS_KEY, SMS_KEY);
|
||||
(void) SectionSingleB (secConfig, SECTION_SMS_SECRET, SMS_SECRET);
|
||||
(void) SectionSingleB (secConfig, SECTION_SMS_TO, SMS_TO);
|
||||
(void) SectionSingleB (secConfig, SECTION_SMS_URL, SMS_URL);
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_VALIDATORS_FILE, strTemp))
|
||||
{
|
||||
VALIDATORS_FILE = strTemp;
|
||||
}
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_DEBUG_LOGFILE, strTemp))
|
||||
DEBUG_LOGFILE = strTemp;
|
||||
|
||||
if (SectionSingleB (secConfig, SECTION_CONSOLE_LOG_OUTPUT, strTemp))
|
||||
CONSOLE_LOG_OUTPUT = strTemp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Config::getSize (SizedItemName item)
|
||||
{
|
||||
SizedItem sizeTable[] = // tiny small medium large huge
|
||||
{
|
||||
|
||||
{ siSweepInterval, { 10, 30, 60, 90, 120 } },
|
||||
|
||||
{ siLedgerFetch, { 2, 2, 3, 3, 3 } },
|
||||
|
||||
{ siValidationsSize, { 256, 256, 512, 1024, 1024 } },
|
||||
{ siValidationsAge, { 500, 500, 500, 500, 500 } },
|
||||
|
||||
{ siNodeCacheSize, { 16384, 32768, 131072, 262144, 0 } },
|
||||
{ siNodeCacheAge, { 60, 90, 120, 900, 0 } },
|
||||
|
||||
{ siTreeCacheSize, { 8192, 65536, 131072, 131072, 0 } },
|
||||
{ siTreeCacheAge, { 30, 60, 90, 120, 900 } },
|
||||
|
||||
{ siSLECacheSize, { 4096, 8192, 16384, 65536, 0 } },
|
||||
{ siSLECacheAge, { 30, 60, 90, 120, 300 } },
|
||||
|
||||
{ siLedgerSize, { 32, 128, 256, 384, 0 } },
|
||||
{ siLedgerAge, { 30, 90, 180, 240, 900 } },
|
||||
|
||||
{ siHashNodeDBCache, { 4, 12, 24, 64, 128 } },
|
||||
{ siTxnDBCache, { 4, 12, 24, 64, 128 } },
|
||||
{ siLgrDBCache, { 4, 8, 16, 32, 128 } },
|
||||
};
|
||||
|
||||
for (int i = 0; i < (sizeof (sizeTable) / sizeof (SizedItem)); ++i)
|
||||
{
|
||||
if (sizeTable[i].item == item)
|
||||
return sizeTable[i].sizes[NODE_SIZE];
|
||||
}
|
||||
|
||||
assert (false);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// VFALCO NOTE Clean members area
|
||||
//
|
||||
|
||||
Config& getConfig ()
|
||||
{
|
||||
static Config config;
|
||||
return config;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
beast::File Config::getConfigDir () const
|
||||
{
|
||||
beast::String const s (CONFIG_FILE.native().c_str ());
|
||||
if (s.isNotEmpty ())
|
||||
return beast::File (s).getParentDirectory ();
|
||||
return beast::File::nonexistent ();
|
||||
}
|
||||
|
||||
beast::File Config::getDatabaseDir () const
|
||||
{
|
||||
beast::String const s (DATA_DIR.native().c_str());
|
||||
if (s.isNotEmpty ())
|
||||
return beast::File (s);
|
||||
return beast::File::nonexistent ();
|
||||
}
|
||||
|
||||
beast::File Config::getValidatorsFile () const
|
||||
{
|
||||
beast::String const s (VALIDATORS_FILE.native().c_str());
|
||||
if (s.isNotEmpty() && getConfigDir() != beast::File::nonexistent())
|
||||
return getConfigDir().getChildFile (s);
|
||||
return beast::File::nonexistent ();
|
||||
}
|
||||
|
||||
beast::URL Config::getValidatorsURL () const
|
||||
{
|
||||
//String s = "https://" + VALIDATORS_SITE + VALIDATORS_URI;
|
||||
beast::String s = VALIDATORS_SITE;
|
||||
return beast::ParsedURL (s).url ();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void Config::setRpcIpAndOptionalPort (std::string const& newAddress)
|
||||
{
|
||||
beast::String const s (newAddress.c_str ());
|
||||
|
||||
int const colonPosition = s.lastIndexOfChar (':');
|
||||
|
||||
if (colonPosition != -1)
|
||||
{
|
||||
beast::String const ipPart = s.substring (0, colonPosition);
|
||||
beast::String const portPart = s.substring (colonPosition + 1, s.length ());
|
||||
|
||||
setRpcIP (ipPart.toRawUTF8 ());
|
||||
setRpcPort (portPart.getIntValue ());
|
||||
}
|
||||
else
|
||||
{
|
||||
setRpcIP (newAddress);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Config::Role Config::getAdminRole (Json::Value const& params, beast::IP::Endpoint const& remoteIp) const
|
||||
{
|
||||
Config::Role role (Config::FORBID);
|
||||
|
||||
bool const bPasswordSupplied =
|
||||
params.isMember ("admin_user") ||
|
||||
params.isMember ("admin_password");
|
||||
|
||||
bool const bPasswordRequired =
|
||||
! this->RPC_ADMIN_USER.empty () ||
|
||||
! this->RPC_ADMIN_PASSWORD.empty ();
|
||||
|
||||
bool bPasswordWrong;
|
||||
|
||||
if (bPasswordSupplied)
|
||||
{
|
||||
if (bPasswordRequired)
|
||||
{
|
||||
// Required, and supplied, check match
|
||||
bPasswordWrong =
|
||||
(this->RPC_ADMIN_USER !=
|
||||
(params.isMember ("admin_user") ? params["admin_user"].asString () : ""))
|
||||
||
|
||||
(this->RPC_ADMIN_PASSWORD !=
|
||||
(params.isMember ("admin_user") ? params["admin_password"].asString () : ""));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not required, but supplied
|
||||
bPasswordWrong = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Required but not supplied,
|
||||
bPasswordWrong = bPasswordRequired;
|
||||
}
|
||||
|
||||
// Meets IP restriction for admin.
|
||||
beast::IP::Endpoint const remote_addr (remoteIp.at_port (0));
|
||||
bool bAdminIP = false;
|
||||
|
||||
for (auto const& allow_addr : RPC_ADMIN_ALLOW)
|
||||
{
|
||||
if (allow_addr == remote_addr)
|
||||
{
|
||||
bAdminIP = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bPasswordWrong // Wrong
|
||||
|| (bPasswordSupplied && !bAdminIP)) // Supplied and doesn't meet IP filter.
|
||||
{
|
||||
role = Config::FORBID;
|
||||
}
|
||||
// If supplied, password is correct.
|
||||
else
|
||||
{
|
||||
// Allow admin, if from admin IP and no password is required or it was supplied and correct.
|
||||
role = bAdminIP && (!bPasswordRequired || bPasswordSupplied) ? Config::ADMIN : Config::GUEST;
|
||||
}
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
beast::File const& Config::getModuleDatabasePath ()
|
||||
{
|
||||
return m_moduleDbPath;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // ripple
|
||||
|
||||
498
src/ripple/module/core/functional/Config.h
Normal file
498
src/ripple/module/core/functional/Config.h
Normal file
@@ -0,0 +1,498 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_CORE_CONFIG_H_INCLUDED
|
||||
#define RIPPLE_CORE_CONFIG_H_INCLUDED
|
||||
|
||||
#include <beast/module/core/files/File.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// VFALCO TODO Replace these with beast "unsigned long long" generators
|
||||
// VFALCO NOTE Apparently these are used elsewhere. Make them constants in the config
|
||||
// or in the Application
|
||||
//
|
||||
#define SYSTEM_CURRENCY_GIFT 1000ull
|
||||
#define SYSTEM_CURRENCY_USERS 100000000ull
|
||||
#define SYSTEM_CURRENCY_PARTS 1000000ull // 10^SYSTEM_CURRENCY_PRECISION
|
||||
#define SYSTEM_CURRENCY_START (SYSTEM_CURRENCY_GIFT*SYSTEM_CURRENCY_USERS*SYSTEM_CURRENCY_PARTS)
|
||||
|
||||
const int DOMAIN_BYTES_MAX = 256;
|
||||
const int PUBLIC_BYTES_MAX = 33; // Maximum bytes for an account public key.
|
||||
|
||||
const int SYSTEM_PEER_PORT = 6561;
|
||||
const int SYSTEM_WEBSOCKET_PORT = 6562;
|
||||
const int SYSTEM_WEBSOCKET_PUBLIC_PORT = 6563; // XXX Going away.
|
||||
|
||||
// Allow anonymous DH.
|
||||
#define DEFAULT_PEER_SSL_CIPHER_LIST "ALL:!LOW:!EXP:!MD5:@STRENGTH"
|
||||
|
||||
// Normal, recommend 1 hour: 60*60
|
||||
// Testing, recommend 1 minute: 60
|
||||
#define DEFAULT_PEER_SCAN_INTERVAL_MIN (60*60) // Seconds
|
||||
|
||||
// Maximum number of peers to try to connect to as client at once.
|
||||
#define DEFAULT_PEER_START_MAX 5
|
||||
|
||||
// Might connect with fewer for testing.
|
||||
#define DEFAULT_PEER_CONNECT_LOW_WATER 10
|
||||
|
||||
#define DEFAULT_PATH_SEARCH_OLD 7
|
||||
#define DEFAULT_PATH_SEARCH 7
|
||||
#define DEFAULT_PATH_SEARCH_FAST 2
|
||||
#define DEFAULT_PATH_SEARCH_MAX 10
|
||||
|
||||
enum SizedItemName
|
||||
{
|
||||
siSweepInterval,
|
||||
siValidationsSize,
|
||||
siValidationsAge,
|
||||
siNodeCacheSize,
|
||||
siNodeCacheAge,
|
||||
siTreeCacheSize,
|
||||
siTreeCacheAge,
|
||||
siSLECacheSize,
|
||||
siSLECacheAge,
|
||||
siLedgerSize,
|
||||
siLedgerAge,
|
||||
siLedgerFetch,
|
||||
siHashNodeDBCache,
|
||||
siTxnDBCache,
|
||||
siLgrDBCache,
|
||||
};
|
||||
|
||||
struct SizedItem
|
||||
{
|
||||
SizedItemName item;
|
||||
int sizes[5];
|
||||
};
|
||||
|
||||
// VFALCO TODO rename all fields to not look like macros, and be more verbose
|
||||
// VFALCO TODO document every member
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// VFALCO NOTE To tame this "Config" beast I am breaking it up into
|
||||
// individual sections related to a specific area of the
|
||||
// program. For example, listening port configuration. Or
|
||||
// node database configuration. Each class has its own
|
||||
// default constructor, and load function for reading in
|
||||
// settings from the parsed config file data.
|
||||
//
|
||||
// Clean member area. Please follow this style for modifying
|
||||
// or adding code in the file.
|
||||
|
||||
struct Helpers
|
||||
{
|
||||
// This replaces CONFIG_FILE_NAME
|
||||
static char const* getConfigFileName ()
|
||||
{
|
||||
return "rippled.cfg";
|
||||
}
|
||||
|
||||
static char const* getDatabaseDirName ()
|
||||
{
|
||||
return "db";
|
||||
}
|
||||
|
||||
static char const* getValidatorsFileName ()
|
||||
{
|
||||
return "validators.txt";
|
||||
}
|
||||
};
|
||||
|
||||
/** The result of performing a load on the parsed config file data.
|
||||
This type is convertible to `bool`.
|
||||
A value of `true` indicates an error occurred,
|
||||
while `false` indicates no error.
|
||||
*/
|
||||
class Error
|
||||
{
|
||||
public:
|
||||
Error () noexcept
|
||||
: m_what (beast::String::empty)
|
||||
, m_fileName ("")
|
||||
, m_lineNumber (0)
|
||||
{
|
||||
}
|
||||
|
||||
Error (beast::String what, char const* fileName, int lineNumber) noexcept
|
||||
: m_what (what)
|
||||
, m_fileName (fileName)
|
||||
, m_lineNumber (lineNumber)
|
||||
{
|
||||
}
|
||||
|
||||
explicit
|
||||
operator bool() const noexcept
|
||||
{
|
||||
return m_what != beast::String::empty;
|
||||
}
|
||||
|
||||
beast::String what () const noexcept
|
||||
{
|
||||
return m_what;
|
||||
}
|
||||
|
||||
char const* fileName () const
|
||||
{
|
||||
return m_fileName;
|
||||
}
|
||||
|
||||
int lineNumber () const
|
||||
{
|
||||
return m_lineNumber;
|
||||
}
|
||||
|
||||
private:
|
||||
beast::String m_what;
|
||||
char const* m_fileName;
|
||||
int m_lineNumber;
|
||||
};
|
||||
|
||||
/** Listening socket settings. */
|
||||
struct DoorSettings
|
||||
{
|
||||
/** Create a default set of door (listening socket) settings. */
|
||||
DoorSettings ();
|
||||
|
||||
/** Load settings from the configuration file. */
|
||||
//Error load (ParsedConfigFile const& file);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Settings related to the configuration file location and directories
|
||||
|
||||
/** Returns the directory from which the configuration file was loaded. */
|
||||
beast::File getConfigDir () const;
|
||||
|
||||
/** Returns the directory in which the current database files are located. */
|
||||
beast::File getDatabaseDir () const;
|
||||
|
||||
// LEGACY FIELDS, REMOVE ASAP
|
||||
boost::filesystem::path CONFIG_FILE; // used by UniqueNodeList
|
||||
private:
|
||||
boost::filesystem::path CONFIG_DIR;
|
||||
public:
|
||||
// VFALCO TODO Make this private and fix callers to go through getDatabaseDir()
|
||||
boost::filesystem::path DATA_DIR;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Settings related to validators
|
||||
|
||||
/** Return the path to the separate, optional validators file. */
|
||||
beast::File getValidatorsFile () const;
|
||||
|
||||
/** Returns the optional URL to a trusted network source of validators. */
|
||||
beast::URL getValidatorsURL () const;
|
||||
|
||||
// DEPRECATED
|
||||
boost::filesystem::path VALIDATORS_FILE; // As specifed in rippled.cfg.
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Settings related to RPC
|
||||
|
||||
/** Get the client or server RPC IP address.
|
||||
@note The string may not always be in a valid parsable state.
|
||||
@return A string representing the address.
|
||||
*/
|
||||
std::string getRpcIP () const { return m_rpcIP; }
|
||||
|
||||
/** Get the client or server RPC port number.
|
||||
@note The port number may be invalid (out of range or zero)
|
||||
@return The RPC port number.
|
||||
*/
|
||||
int getRpcPort () const { return m_rpcPort; }
|
||||
|
||||
/** Set the client or server RPC IP and optional port.
|
||||
@note The string is not syntax checked.
|
||||
@param newAddress A string in the format <ip-address>[':'<port-number>]
|
||||
*/
|
||||
void setRpcIpAndOptionalPort (std::string const& newAddress);
|
||||
|
||||
/** Set the client or server RPC IP.
|
||||
@note The string is not syntax-checked.
|
||||
@param newIP A string representing the IP address to use.
|
||||
*/
|
||||
void setRpcIP (std::string const& newIP) { m_rpcIP = newIP; }
|
||||
|
||||
/** Set the client or server RPC port number.
|
||||
@note The port number is not range checked.
|
||||
@param newPort The RPC port number to use.
|
||||
*/
|
||||
void setRpcPort (int newPort) { m_rpcPort = newPort; }
|
||||
|
||||
/** Convert the RPC/port combination to a readable string.
|
||||
*/
|
||||
beast::String const getRpcAddress ()
|
||||
{
|
||||
beast::String s;
|
||||
|
||||
s << m_rpcIP.c_str () << ":" << m_rpcPort;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/** Determine the level of administrative permission to grant.
|
||||
*/
|
||||
enum Role
|
||||
{
|
||||
GUEST,
|
||||
USER,
|
||||
ADMIN,
|
||||
FORBID
|
||||
};
|
||||
Role getAdminRole (Json::Value const& params, beast::IP::Endpoint const& remoteIp) const;
|
||||
|
||||
/** Listening port number for peer connections. */
|
||||
int peerListeningPort;
|
||||
|
||||
/** PROXY listening port number
|
||||
If this is not zero, it indicates an additional port number on
|
||||
which we should accept incoming Peer connections that will also
|
||||
require a PROXY handshake.
|
||||
|
||||
The PROXY Protocol:
|
||||
http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt
|
||||
*/
|
||||
int peerPROXYListeningPort;
|
||||
|
||||
/** List of Validators entries from rippled.cfg */
|
||||
std::vector <std::string> validators;
|
||||
|
||||
private:
|
||||
std::string m_rpcIP;
|
||||
int m_rpcPort; // VFALCO TODO This should be a short.
|
||||
|
||||
private:
|
||||
/** The folder where new module databases should be located */
|
||||
beast::File m_moduleDbPath;
|
||||
|
||||
public:
|
||||
//--------------------------------------------------------------------------
|
||||
/** Returns the location were databases should be located
|
||||
The location may be a file, in which case databases should be placed in
|
||||
the file, or it may be a directory, in which cases databases should be
|
||||
stored in a file named after the module (e.g. "peerfinder.sqlite") that
|
||||
is inside that directory.
|
||||
*/
|
||||
beast::File const& getModuleDatabasePath ();
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Parameters for the insight collection module */
|
||||
beast::StringPairArray insightSettings;
|
||||
|
||||
/** Parameters for the main NodeStore database.
|
||||
|
||||
This is 1 or more strings of the form <key>=<value>
|
||||
The 'type' and 'path' keys are required, see rippled-example.cfg
|
||||
|
||||
@see Database
|
||||
*/
|
||||
beast::StringPairArray nodeDatabase;
|
||||
|
||||
/** Parameters for the ephemeral NodeStore database.
|
||||
|
||||
This is an auxiliary database for the NodeStore, usually placed
|
||||
on a separate faster volume. However, the volume data may not persist
|
||||
between launches. Use of the ephemeral database is optional.
|
||||
|
||||
The format is the same as that for @ref nodeDatabase
|
||||
|
||||
@see Database
|
||||
*/
|
||||
beast::StringPairArray ephemeralNodeDatabase;
|
||||
|
||||
/** Parameters for importing an old database in to the current node database.
|
||||
If this is not empty, then it specifies the key/value parameters for
|
||||
another node database from which to import all data into the current
|
||||
node database specified by @ref nodeDatabase.
|
||||
The format of this string is in the form:
|
||||
<key>'='<value>['|'<key>'='value]
|
||||
@see parseDelimitedKeyValueString
|
||||
*/
|
||||
bool doImport;
|
||||
beast::StringPairArray importNodeDatabase;
|
||||
|
||||
//
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
public:
|
||||
// Configuration parameters
|
||||
bool QUIET;
|
||||
|
||||
boost::filesystem::path DEBUG_LOGFILE;
|
||||
std::string CONSOLE_LOG_OUTPUT;
|
||||
|
||||
bool ELB_SUPPORT; // Support Amazon ELB
|
||||
|
||||
std::string VALIDATORS_SITE; // Where to find validators.txt on the Internet.
|
||||
std::string VALIDATORS_URI; // URI of validators.txt.
|
||||
std::string VALIDATORS_BASE; // Name
|
||||
std::vector<std::string> IPS; // Peer IPs from rippled.cfg.
|
||||
std::vector<std::string> IPS_FIXED; // Fixed Peer IPs from rippled.cfg.
|
||||
std::vector<std::string> SNTP_SERVERS; // SNTP servers from rippled.cfg.
|
||||
|
||||
enum StartUpType
|
||||
{
|
||||
FRESH,
|
||||
NORMAL,
|
||||
LOAD,
|
||||
LOAD_FILE,
|
||||
REPLAY,
|
||||
NETWORK
|
||||
};
|
||||
StartUpType START_UP;
|
||||
|
||||
|
||||
|
||||
std::string START_LEDGER;
|
||||
|
||||
// Database
|
||||
std::string DATABASE_PATH;
|
||||
|
||||
// Network parameters
|
||||
int NETWORK_START_TIME; // The Unix time we start ledger 0.
|
||||
int TRANSACTION_FEE_BASE; // The number of fee units a reference transaction costs
|
||||
int LEDGER_SECONDS;
|
||||
int LEDGER_PROPOSAL_DELAY_SECONDS;
|
||||
int LEDGER_AVALANCHE_SECONDS;
|
||||
bool LEDGER_CREATOR; // Should be false unless we are starting a new ledger.
|
||||
|
||||
/** Operate in stand-alone mode.
|
||||
|
||||
In stand alone mode:
|
||||
|
||||
- Peer connections are not attempted or accepted
|
||||
- The ledger is not advanced automatically.
|
||||
- If no ledger is loaded, the default ledger with the root
|
||||
account is created.
|
||||
*/
|
||||
bool RUN_STANDALONE;
|
||||
|
||||
// Note: The following parameters do not relate to the UNL or trust at all
|
||||
unsigned int NETWORK_QUORUM; // Minimum number of nodes to consider the network present
|
||||
int VALIDATION_QUORUM; // Minimum validations to consider ledger authoritative
|
||||
|
||||
// Peer networking parameters
|
||||
std::string PEER_IP;
|
||||
int NUMBER_CONNECTIONS;
|
||||
std::string PEER_SSL_CIPHER_LIST;
|
||||
int PEER_SCAN_INTERVAL_MIN;
|
||||
int PEER_START_MAX;
|
||||
unsigned int PEER_CONNECT_LOW_WATER;
|
||||
bool PEER_PRIVATE; // True to ask peers not to relay current IP.
|
||||
unsigned int PEERS_MAX;
|
||||
|
||||
// Websocket networking parameters
|
||||
std::string WEBSOCKET_PUBLIC_IP; // XXX Going away. Merge with the inbound peer connction.
|
||||
int WEBSOCKET_PUBLIC_PORT;
|
||||
int WEBSOCKET_PUBLIC_SECURE;
|
||||
|
||||
std::string WEBSOCKET_PROXY_IP; // XXX Going away. Merge with the inbound peer connction.
|
||||
int WEBSOCKET_PROXY_PORT;
|
||||
int WEBSOCKET_PROXY_SECURE;
|
||||
|
||||
std::string WEBSOCKET_IP;
|
||||
int WEBSOCKET_PORT;
|
||||
int WEBSOCKET_SECURE;
|
||||
|
||||
int WEBSOCKET_PING_FREQ;
|
||||
|
||||
std::string WEBSOCKET_SSL_CERT;
|
||||
std::string WEBSOCKET_SSL_CHAIN;
|
||||
std::string WEBSOCKET_SSL_KEY;
|
||||
|
||||
// RPC parameters
|
||||
std::vector<beast::IP::Endpoint> RPC_ADMIN_ALLOW;
|
||||
std::string RPC_ADMIN_PASSWORD;
|
||||
std::string RPC_ADMIN_USER;
|
||||
std::string RPC_PASSWORD;
|
||||
std::string RPC_USER;
|
||||
bool RPC_ALLOW_REMOTE;
|
||||
Json::Value RPC_STARTUP;
|
||||
|
||||
int RPC_SECURE;
|
||||
std::string RPC_SSL_CERT;
|
||||
std::string RPC_SSL_CHAIN;
|
||||
std::string RPC_SSL_KEY;
|
||||
|
||||
// Path searching
|
||||
int PATH_SEARCH_OLD;
|
||||
int PATH_SEARCH;
|
||||
int PATH_SEARCH_FAST;
|
||||
int PATH_SEARCH_MAX;
|
||||
|
||||
// Validation
|
||||
RippleAddress VALIDATION_SEED, VALIDATION_PUB, VALIDATION_PRIV;
|
||||
|
||||
// Node/Cluster
|
||||
std::vector<std::string> CLUSTER_NODES;
|
||||
RippleAddress NODE_SEED, NODE_PUB, NODE_PRIV;
|
||||
|
||||
// Fee schedule (All below values are in fee units)
|
||||
std::uint64_t FEE_DEFAULT; // Default fee.
|
||||
std::uint64_t FEE_ACCOUNT_RESERVE; // Amount of units not allowed to send.
|
||||
std::uint64_t FEE_OWNER_RESERVE; // Amount of units not allowed to send per owner entry.
|
||||
std::uint64_t FEE_NICKNAME_CREATE; // Fee to create a nickname.
|
||||
std::uint64_t FEE_OFFER; // Rate per day.
|
||||
int FEE_CONTRACT_OPERATION; // fee for each contract operation
|
||||
|
||||
// Node storage configuration
|
||||
std::uint32_t LEDGER_HISTORY;
|
||||
std::uint32_t FETCH_DEPTH;
|
||||
int NODE_SIZE;
|
||||
|
||||
// Client behavior
|
||||
int ACCOUNT_PROBE_MAX; // How far to scan for accounts.
|
||||
|
||||
// Signing signatures.
|
||||
std::uint32_t SIGN_TRANSACTION;
|
||||
std::uint32_t SIGN_VALIDATION;
|
||||
std::uint32_t SIGN_PROPOSAL;
|
||||
|
||||
bool SSL_VERIFY;
|
||||
std::string SSL_VERIFY_FILE;
|
||||
std::string SSL_VERIFY_DIR;
|
||||
|
||||
std::string SMS_FROM;
|
||||
std::string SMS_KEY;
|
||||
std::string SMS_SECRET;
|
||||
std::string SMS_TO;
|
||||
std::string SMS_URL;
|
||||
|
||||
public:
|
||||
Config ();
|
||||
|
||||
int getSize (SizedItemName);
|
||||
void setup (const std::string& strConf, bool bQuiet);
|
||||
void load ();
|
||||
};
|
||||
|
||||
extern Config& getConfig ();
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
115
src/ripple/module/core/functional/ConfigSections.h
Normal file
115
src/ripple/module/core/functional/ConfigSections.h
Normal file
@@ -0,0 +1,115 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_CONFIGSECTIONS_H_INCLUDED
|
||||
#define RIPPLE_CONFIGSECTIONS_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// VFALCO NOTE
|
||||
//
|
||||
// Please use this style for all new sections
|
||||
// And if you're feeling generous, convert all the
|
||||
// existing macros to this format as well.
|
||||
//
|
||||
struct ConfigSection
|
||||
{
|
||||
static beast::String nodeDatabase () { return "node_db"; }
|
||||
static beast::String tempNodeDatabase () { return "temp_db"; }
|
||||
static beast::String importNodeDatabase () { return "import_db"; }
|
||||
};
|
||||
|
||||
// VFALCO TODO Rename and replace these macros with variables.
|
||||
#define SECTION_ACCOUNT_PROBE_MAX "account_probe_max"
|
||||
#define SECTION_CLUSTER_NODES "cluster_nodes"
|
||||
#define SECTION_DATABASE_PATH "database_path"
|
||||
#define SECTION_DEBUG_LOGFILE "debug_logfile"
|
||||
#define SECTION_CONSOLE_LOG_OUTPUT "console_log_output"
|
||||
#define SECTION_ELB_SUPPORT "elb_support"
|
||||
#define SECTION_FEE_DEFAULT "fee_default"
|
||||
#define SECTION_FEE_NICKNAME_CREATE "fee_nickname_create"
|
||||
#define SECTION_FEE_OFFER "fee_offer"
|
||||
#define SECTION_FEE_OPERATION "fee_operation"
|
||||
#define SECTION_FEE_ACCOUNT_RESERVE "fee_account_reserve"
|
||||
#define SECTION_FEE_OWNER_RESERVE "fee_owner_reserve"
|
||||
#define SECTION_FETCH_DEPTH "fetch_depth"
|
||||
#define SECTION_LEDGER_HISTORY "ledger_history"
|
||||
#define SECTION_INSIGHT "insight"
|
||||
#define SECTION_IPS "ips"
|
||||
#define SECTION_IPS_FIXED "ips_fixed"
|
||||
#define SECTION_NETWORK_QUORUM "network_quorum"
|
||||
#define SECTION_NODE_SEED "node_seed"
|
||||
#define SECTION_NODE_SIZE "node_size"
|
||||
#define SECTION_PATH_SEARCH_OLD "path_search_old"
|
||||
#define SECTION_PATH_SEARCH "path_search"
|
||||
#define SECTION_PATH_SEARCH_FAST "path_search_fast"
|
||||
#define SECTION_PATH_SEARCH_MAX "path_search_max"
|
||||
#define SECTION_PEER_CONNECT_LOW_WATER "peer_connect_low_water"
|
||||
#define SECTION_PEER_IP "peer_ip"
|
||||
#define SECTION_PEER_PORT "peer_port"
|
||||
#define SECTION_PEER_PROXY_PORT "peer_port_proxy"
|
||||
#define SECTION_PEER_PRIVATE "peer_private"
|
||||
#define SECTION_PEERS_MAX "peers_max"
|
||||
#define SECTION_PEER_SCAN_INTERVAL_MIN "peer_scan_interval_min"
|
||||
#define SECTION_PEER_SSL_CIPHER_LIST "peer_ssl_cipher_list"
|
||||
#define SECTION_PEER_START_MAX "peer_start_max"
|
||||
#define SECTION_RPC_ALLOW_REMOTE "rpc_allow_remote"
|
||||
#define SECTION_RPC_ADMIN_ALLOW "rpc_admin_allow"
|
||||
#define SECTION_RPC_ADMIN_USER "rpc_admin_user"
|
||||
#define SECTION_RPC_ADMIN_PASSWORD "rpc_admin_password"
|
||||
#define SECTION_RPC_IP "rpc_ip"
|
||||
#define SECTION_RPC_PORT "rpc_port"
|
||||
#define SECTION_RPC_USER "rpc_user"
|
||||
#define SECTION_RPC_PASSWORD "rpc_password"
|
||||
#define SECTION_RPC_STARTUP "rpc_startup"
|
||||
#define SECTION_RPC_SECURE "rpc_secure"
|
||||
#define SECTION_RPC_SSL_CERT "rpc_ssl_cert"
|
||||
#define SECTION_RPC_SSL_CHAIN "rpc_ssl_chain"
|
||||
#define SECTION_RPC_SSL_KEY "rpc_ssl_key"
|
||||
#define SECTION_SMS_FROM "sms_from"
|
||||
#define SECTION_SMS_KEY "sms_key"
|
||||
#define SECTION_SMS_SECRET "sms_secret"
|
||||
#define SECTION_SMS_TO "sms_to"
|
||||
#define SECTION_SMS_URL "sms_url"
|
||||
#define SECTION_SNTP "sntp_servers"
|
||||
#define SECTION_SSL_VERIFY "ssl_verify"
|
||||
#define SECTION_SSL_VERIFY_FILE "ssl_verify_file"
|
||||
#define SECTION_SSL_VERIFY_DIR "ssl_verify_dir"
|
||||
#define SECTION_VALIDATORS_FILE "validators_file"
|
||||
#define SECTION_VALIDATION_QUORUM "validation_quorum"
|
||||
#define SECTION_VALIDATION_SEED "validation_seed"
|
||||
#define SECTION_WEBSOCKET_PUBLIC_IP "websocket_public_ip"
|
||||
#define SECTION_WEBSOCKET_PUBLIC_PORT "websocket_public_port"
|
||||
#define SECTION_WEBSOCKET_PUBLIC_SECURE "websocket_public_secure"
|
||||
#define SECTION_WEBSOCKET_PROXY_IP "websocket_proxy_ip"
|
||||
#define SECTION_WEBSOCKET_PROXY_PORT "websocket_proxy_port"
|
||||
#define SECTION_WEBSOCKET_PROXY_SECURE "websocket_proxy_secure"
|
||||
#define SECTION_WEBSOCKET_PING_FREQ "websocket_ping_frequency"
|
||||
#define SECTION_WEBSOCKET_IP "websocket_ip"
|
||||
#define SECTION_WEBSOCKET_PORT "websocket_port"
|
||||
#define SECTION_WEBSOCKET_SECURE "websocket_secure"
|
||||
#define SECTION_WEBSOCKET_SSL_CERT "websocket_ssl_cert"
|
||||
#define SECTION_WEBSOCKET_SSL_CHAIN "websocket_ssl_chain"
|
||||
#define SECTION_WEBSOCKET_SSL_KEY "websocket_ssl_key"
|
||||
#define SECTION_VALIDATORS "validators"
|
||||
#define SECTION_VALIDATORS_SITE "validators_site"
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
130
src/ripple/module/core/functional/Job.cpp
Normal file
130
src/ripple/module/core/functional/Job.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
namespace ripple {
|
||||
|
||||
Job::Job ()
|
||||
: mType (jtINVALID)
|
||||
, mJobIndex (0)
|
||||
{
|
||||
}
|
||||
|
||||
Job::Job (JobType type, std::uint64_t index)
|
||||
: mType (type)
|
||||
, mJobIndex (index)
|
||||
{
|
||||
}
|
||||
|
||||
Job::Job (JobType type,
|
||||
std::string const& name,
|
||||
std::uint64_t index,
|
||||
LoadMonitor& lm,
|
||||
std::function <void (Job&)> const& job,
|
||||
CancelCallback cancelCallback)
|
||||
: m_cancelCallback (cancelCallback)
|
||||
, mType (type)
|
||||
, mJobIndex (index)
|
||||
, mJob (job)
|
||||
, mName (name)
|
||||
, m_queue_time (clock_type::now ())
|
||||
{
|
||||
m_loadEvent = boost::make_shared <LoadEvent> (boost::ref (lm), name, false);
|
||||
}
|
||||
|
||||
JobType Job::getType () const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
CancelCallback Job::getCancelCallback () const
|
||||
{
|
||||
bassert (m_cancelCallback);
|
||||
return m_cancelCallback;
|
||||
}
|
||||
|
||||
Job::clock_type::time_point const& Job::queue_time () const
|
||||
{
|
||||
return m_queue_time;
|
||||
}
|
||||
|
||||
bool Job::shouldCancel () const
|
||||
{
|
||||
if (m_cancelCallback)
|
||||
return m_cancelCallback ();
|
||||
return false;
|
||||
}
|
||||
|
||||
void Job::doJob ()
|
||||
{
|
||||
m_loadEvent->start ();
|
||||
m_loadEvent->reName (mName);
|
||||
|
||||
mJob (*this);
|
||||
}
|
||||
|
||||
void Job::rename (std::string const& newName)
|
||||
{
|
||||
mName = newName;
|
||||
}
|
||||
|
||||
bool Job::operator> (const Job& j) const
|
||||
{
|
||||
if (mType < j.mType)
|
||||
return true;
|
||||
|
||||
if (mType > j.mType)
|
||||
return false;
|
||||
|
||||
return mJobIndex > j.mJobIndex;
|
||||
}
|
||||
|
||||
bool Job::operator>= (const Job& j) const
|
||||
{
|
||||
if (mType < j.mType)
|
||||
return true;
|
||||
|
||||
if (mType > j.mType)
|
||||
return false;
|
||||
|
||||
return mJobIndex >= j.mJobIndex;
|
||||
}
|
||||
|
||||
bool Job::operator< (const Job& j) const
|
||||
{
|
||||
if (mType < j.mType)
|
||||
return false;
|
||||
|
||||
if (mType > j.mType)
|
||||
return true;
|
||||
|
||||
return mJobIndex < j.mJobIndex;
|
||||
}
|
||||
|
||||
bool Job::operator<= (const Job& j) const
|
||||
{
|
||||
if (mType < j.mType)
|
||||
return false;
|
||||
|
||||
if (mType > j.mType)
|
||||
return true;
|
||||
|
||||
return mJobIndex <= j.mJobIndex;
|
||||
}
|
||||
|
||||
}
|
||||
143
src/ripple/module/core/functional/Job.h
Normal file
143
src/ripple/module/core/functional/Job.h
Normal file
@@ -0,0 +1,143 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_JOB_H
|
||||
#define RIPPLE_JOB_H
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Note that this queue should only be used for CPU-bound jobs
|
||||
// It is primarily intended for signature checking
|
||||
|
||||
enum JobType
|
||||
{
|
||||
// Special type indicating an invalid job - will go away soon.
|
||||
jtINVALID = -1,
|
||||
|
||||
// Job types - the position in this enum indicates the job priority with
|
||||
// earlier jobs having lower priority than later jobs. If you wish to
|
||||
// insert a job at a specific priority, simply add it at the right location.
|
||||
|
||||
jtPACK, // Make a fetch pack for a peer
|
||||
jtPUBOLDLEDGER, // An old ledger has been accepted
|
||||
jtVALIDATION_ut, // A validation from an untrusted source
|
||||
jtPROOFWORK, // A proof of work demand from another server
|
||||
jtTRANSACTION_l, // A local transaction
|
||||
jtPROPOSAL_ut, // A proposal from an untrusted source
|
||||
jtLEDGER_DATA, // Received data for a ledger we're acquiring
|
||||
jtCLIENT, // A websocket command from the client
|
||||
jtRPC, // A websocket command from the client
|
||||
jtUPDATE_PF, // Update pathfinding requests
|
||||
jtTRANSACTION, // A transaction received from the network
|
||||
jtUNL, // A Score or Fetch of the UNL (DEPRECATED)
|
||||
jtADVANCE, // Advance validated/acquired ledgers
|
||||
jtPUBLEDGER, // Publish a fully-accepted ledger
|
||||
jtTXN_DATA, // Fetch a proposed set
|
||||
jtWAL, // Write-ahead logging
|
||||
jtVALIDATION_t, // A validation from a trusted source
|
||||
jtWRITE, // Write out hashed objects
|
||||
jtACCEPT, // Accept a consensus ledger
|
||||
jtPROPOSAL_t, // A proposal from a trusted source
|
||||
jtSWEEP, // Sweep for stale structures
|
||||
jtNETOP_CLUSTER, // NetworkOPs cluster peer report
|
||||
jtNETOP_TIMER, // NetworkOPs net timer processing
|
||||
jtADMIN, // An administrative operation
|
||||
|
||||
// Special job types which are not dispatched by the job pool
|
||||
jtPEER ,
|
||||
jtDISK ,
|
||||
jtTXN_PROC ,
|
||||
jtOB_SETUP ,
|
||||
jtPATH_FIND ,
|
||||
jtHO_READ ,
|
||||
jtHO_WRITE ,
|
||||
jtGENERIC , // Used just to measure time
|
||||
|
||||
// Node store monitoring
|
||||
jtNS_SYNC_READ ,
|
||||
jtNS_ASYNC_READ ,
|
||||
jtNS_WRITE ,
|
||||
};
|
||||
|
||||
class Job
|
||||
{
|
||||
public:
|
||||
typedef std::chrono::steady_clock clock_type;
|
||||
|
||||
/** Default constructor.
|
||||
|
||||
Allows Job to be used as a container type.
|
||||
|
||||
This is used to allow things like jobMap [key] = value.
|
||||
*/
|
||||
// VFALCO NOTE I'd prefer not to have a default constructed object.
|
||||
// What is the semantic meaning of a Job with no associated
|
||||
// function? Having the invariant "all Job objects refer to
|
||||
// a job" would reduce the number of states.
|
||||
//
|
||||
Job ();
|
||||
|
||||
//Job (Job const& other);
|
||||
|
||||
Job (JobType type, std::uint64_t index);
|
||||
|
||||
// VFALCO TODO try to remove the dependency on LoadMonitor.
|
||||
Job (JobType type,
|
||||
std::string const& name,
|
||||
std::uint64_t index,
|
||||
LoadMonitor& lm,
|
||||
std::function <void (Job&)> const& job,
|
||||
CancelCallback cancelCallback);
|
||||
|
||||
//Job& operator= (Job const& other);
|
||||
|
||||
JobType getType () const;
|
||||
|
||||
CancelCallback getCancelCallback () const;
|
||||
|
||||
/** Returns the time when the job was queued. */
|
||||
clock_type::time_point const& queue_time () const;
|
||||
|
||||
/** Returns `true` if the running job should make a best-effort cancel. */
|
||||
bool shouldCancel () const;
|
||||
|
||||
void doJob ();
|
||||
|
||||
void rename (const std::string& n);
|
||||
|
||||
// These comparison operators make the jobs sort in priority order
|
||||
// in the job set
|
||||
bool operator< (const Job& j) const;
|
||||
bool operator> (const Job& j) const;
|
||||
bool operator<= (const Job& j) const;
|
||||
bool operator>= (const Job& j) const;
|
||||
|
||||
private:
|
||||
CancelCallback m_cancelCallback;
|
||||
JobType mType;
|
||||
std::uint64_t mJobIndex;
|
||||
std::function <void (Job&)> mJob;
|
||||
LoadEvent::pointer m_loadEvent;
|
||||
std::string mName;
|
||||
clock_type::time_point m_queue_time;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
691
src/ripple/module/core/functional/JobQueue.cpp
Normal file
691
src/ripple/module/core/functional/JobQueue.cpp
Normal file
@@ -0,0 +1,691 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/module/core/functional/JobQueue.h>
|
||||
#include <ripple/module/core/functional/JobTypes.h>
|
||||
#include <ripple/module/core/functional/JobTypeInfo.h>
|
||||
#include <ripple/module/core/functional/JobTypeData.h>
|
||||
|
||||
#include <beast/cxx14/memory.h>
|
||||
#include <beast/chrono/chrono_util.h>
|
||||
#include <beast/module/core/thread/Workers.h>
|
||||
#include <beast/module/core/system/SystemStats.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class JobQueueImp
|
||||
: public JobQueue
|
||||
, private beast::Workers::Callback
|
||||
{
|
||||
public:
|
||||
typedef std::set <Job> JobSet;
|
||||
typedef std::map <JobType, JobTypeData> JobDataMap;
|
||||
typedef beast::CriticalSection::ScopedLockType ScopedLock;
|
||||
|
||||
beast::Journal m_journal;
|
||||
beast::CriticalSection m_mutex;
|
||||
std::uint64_t m_lastJob;
|
||||
JobSet m_jobSet;
|
||||
JobDataMap m_jobData;
|
||||
JobTypeData m_invalidJobData;
|
||||
|
||||
// The number of jobs currently in processTask()
|
||||
int m_processCount;
|
||||
|
||||
beast::Workers m_workers;
|
||||
CancelCallback m_cancelCallback;
|
||||
|
||||
// statistics tracking
|
||||
beast::insight::Collector::ptr m_collector;
|
||||
beast::insight::Gauge job_count;
|
||||
beast::insight::Hook hook;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
static JobTypes const& getJobTypes ()
|
||||
{
|
||||
static JobTypes types;
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
JobQueueImp (beast::insight::Collector::ptr const& collector,
|
||||
Stoppable& parent, beast::Journal journal)
|
||||
: JobQueue ("JobQueue", parent)
|
||||
, m_journal (journal)
|
||||
, m_lastJob (0)
|
||||
, m_invalidJobData (getJobTypes ().getInvalid (), collector)
|
||||
, m_processCount (0)
|
||||
, m_workers (*this, "JobQueue", 0)
|
||||
, m_cancelCallback (boost::bind (&Stoppable::isStopping, this))
|
||||
, m_collector (collector)
|
||||
{
|
||||
hook = m_collector->make_hook (std::bind (
|
||||
&JobQueueImp::collect, this));
|
||||
job_count = m_collector->make_gauge ("job_count");
|
||||
|
||||
{
|
||||
ScopedLock lock (m_mutex);
|
||||
|
||||
for (auto const& x : getJobTypes ())
|
||||
{
|
||||
JobTypeInfo const& jt = x.second;
|
||||
|
||||
// And create dynamic information for all jobs
|
||||
auto const result (m_jobData.emplace (std::piecewise_construct,
|
||||
std::forward_as_tuple (jt.type ()),
|
||||
std::forward_as_tuple (jt, m_collector)));
|
||||
assert (result.second == true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~JobQueueImp ()
|
||||
{
|
||||
// Must unhook before destroying
|
||||
hook = beast::insight::Hook ();
|
||||
}
|
||||
|
||||
void collect ()
|
||||
{
|
||||
ScopedLock lock (m_mutex);
|
||||
job_count = m_jobSet.size ();
|
||||
}
|
||||
|
||||
void addJob (JobType type, std::string const& name,
|
||||
boost::function <void (Job&)> const& jobFunc)
|
||||
{
|
||||
assert (type != jtINVALID);
|
||||
|
||||
JobDataMap::iterator iter (m_jobData.find (type));
|
||||
assert (iter != m_jobData.end ());
|
||||
|
||||
if (iter == m_jobData.end ())
|
||||
return;
|
||||
|
||||
JobTypeData& data (iter->second);
|
||||
|
||||
// FIXME: Workaround incorrect client shutdown ordering
|
||||
// do not add jobs to a queue with no threads
|
||||
assert (type == jtCLIENT || m_workers.getNumberOfThreads () > 0);
|
||||
|
||||
{
|
||||
// If this goes off it means that a child didn't follow
|
||||
// the Stoppable API rules. A job may only be added if:
|
||||
//
|
||||
// - The JobQueue has NOT stopped
|
||||
// AND
|
||||
// * We are currently processing jobs
|
||||
// OR
|
||||
// * We have have pending jobs
|
||||
// OR
|
||||
// * Not all children are stopped
|
||||
//
|
||||
ScopedLock lock (m_mutex);
|
||||
assert (! isStopped() && (
|
||||
m_processCount>0 ||
|
||||
! m_jobSet.empty () ||
|
||||
! areChildrenStopped()));
|
||||
}
|
||||
|
||||
// Don't even add it to the queue if we're stopping
|
||||
// and the job type is marked for skipOnStop.
|
||||
//
|
||||
if (isStopping() && skipOnStop (type))
|
||||
{
|
||||
m_journal.debug <<
|
||||
"Skipping addJob ('" << name << "')";
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
ScopedLock lock (m_mutex);
|
||||
|
||||
std::pair <std::set <Job>::iterator, bool> result (
|
||||
m_jobSet.insert (Job (type, name, ++m_lastJob,
|
||||
data.load (), jobFunc, m_cancelCallback)));
|
||||
queueJob (*result.first, lock);
|
||||
}
|
||||
}
|
||||
|
||||
int getJobCount (JobType t)
|
||||
{
|
||||
ScopedLock lock (m_mutex);
|
||||
|
||||
JobDataMap::const_iterator c = m_jobData.find (t);
|
||||
|
||||
return (c == m_jobData.end ())
|
||||
? 0
|
||||
: c->second.waiting;
|
||||
}
|
||||
|
||||
int getJobCountTotal (JobType t)
|
||||
{
|
||||
ScopedLock lock (m_mutex);
|
||||
|
||||
JobDataMap::const_iterator c = m_jobData.find (t);
|
||||
|
||||
return (c == m_jobData.end ())
|
||||
? 0
|
||||
: (c->second.waiting + c->second.running);
|
||||
}
|
||||
|
||||
int getJobCountGE (JobType t)
|
||||
{
|
||||
// return the number of jobs at this priority level or greater
|
||||
int ret = 0;
|
||||
|
||||
ScopedLock lock (m_mutex);
|
||||
|
||||
for (auto const& x : m_jobData)
|
||||
{
|
||||
if (x.first >= t)
|
||||
ret += x.second.waiting;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// shut down the job queue without completing pending jobs
|
||||
//
|
||||
void shutdown ()
|
||||
{
|
||||
m_journal.info << "Job queue shutting down";
|
||||
|
||||
m_workers.pauseAllThreadsAndWait ();
|
||||
}
|
||||
|
||||
// set the number of thread serving the job queue to precisely this number
|
||||
void setThreadCount (int c, bool const standaloneMode)
|
||||
{
|
||||
if (standaloneMode)
|
||||
{
|
||||
c = 1;
|
||||
}
|
||||
else if (c == 0)
|
||||
{
|
||||
c = beast::SystemStats::getNumCpus ();
|
||||
|
||||
// VFALCO NOTE According to boost, hardware_concurrency cannot return
|
||||
// negative numbers/
|
||||
//
|
||||
if (c < 0)
|
||||
c = 2; // VFALCO NOTE Why 2?
|
||||
|
||||
if (c > 4) // I/O will bottleneck
|
||||
c = 4;
|
||||
|
||||
c += 2;
|
||||
|
||||
m_journal.info << "Auto-tuning to " << c <<
|
||||
" validation/transaction/proposal threads";
|
||||
}
|
||||
|
||||
m_workers.setNumberOfThreads (c);
|
||||
}
|
||||
|
||||
|
||||
LoadEvent::pointer getLoadEvent (JobType t, const std::string& name)
|
||||
{
|
||||
JobDataMap::iterator iter (m_jobData.find (t));
|
||||
assert (iter != m_jobData.end ());
|
||||
|
||||
if (iter == m_jobData.end ())
|
||||
return boost::shared_ptr<LoadEvent> ();
|
||||
|
||||
return boost::make_shared<LoadEvent> (
|
||||
boost::ref (iter-> second.load ()), name, true);
|
||||
}
|
||||
|
||||
LoadEvent::autoptr getLoadEventAP (JobType t, const std::string& name)
|
||||
{
|
||||
JobDataMap::iterator iter (m_jobData.find (t));
|
||||
assert (iter != m_jobData.end ());
|
||||
|
||||
if (iter == m_jobData.end ())
|
||||
return LoadEvent::autoptr ();
|
||||
|
||||
return LoadEvent::autoptr (
|
||||
new LoadEvent (iter-> second.load (), name, true));
|
||||
}
|
||||
|
||||
void addLoadEvents (JobType t,
|
||||
int count, std::chrono::milliseconds elapsed)
|
||||
{
|
||||
JobDataMap::iterator iter (m_jobData.find (t));
|
||||
assert (iter != m_jobData.end ());
|
||||
iter->second.load().addSamples (count, elapsed);
|
||||
}
|
||||
|
||||
bool isOverloaded ()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (auto& x : m_jobData)
|
||||
{
|
||||
if (x.second.load ().isOver ())
|
||||
++count;
|
||||
}
|
||||
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
Json::Value getJson (int)
|
||||
{
|
||||
Json::Value ret (Json::objectValue);
|
||||
|
||||
ret["threads"] = m_workers.getNumberOfThreads ();
|
||||
|
||||
Json::Value priorities = Json::arrayValue;
|
||||
|
||||
ScopedLock lock (m_mutex);
|
||||
|
||||
for (auto& x : m_jobData)
|
||||
{
|
||||
assert (x.first != jtINVALID);
|
||||
|
||||
if (x.first == jtGENERIC)
|
||||
continue;
|
||||
|
||||
JobTypeData& data (x.second);
|
||||
|
||||
LoadMonitor::Stats stats (data.stats ());
|
||||
|
||||
int waiting (data.waiting);
|
||||
int running (data.running);
|
||||
|
||||
if ((stats.count != 0) || (waiting != 0) ||
|
||||
(stats.latencyPeak != 0) || (running != 0))
|
||||
{
|
||||
Json::Value& pri = priorities.append (Json::objectValue);
|
||||
|
||||
pri["job_type"] = data.name ();
|
||||
|
||||
if (stats.isOverloaded)
|
||||
pri["over_target"] = true;
|
||||
|
||||
if (waiting != 0)
|
||||
pri["waiting"] = waiting;
|
||||
|
||||
if (stats.count != 0)
|
||||
pri["per_second"] = static_cast<int> (stats.count);
|
||||
|
||||
if (stats.latencyPeak != 0)
|
||||
pri["peak_time"] = static_cast<int> (stats.latencyPeak);
|
||||
|
||||
if (stats.latencyAvg != 0)
|
||||
pri["avg_time"] = static_cast<int> (stats.latencyAvg);
|
||||
|
||||
if (running != 0)
|
||||
pri["in_progress"] = running;
|
||||
}
|
||||
}
|
||||
|
||||
ret["job_types"] = priorities;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
//--------------------------------------------------------------------------
|
||||
JobTypeData& getJobTypeData (JobType type)
|
||||
{
|
||||
JobDataMap::iterator c (m_jobData.find (type));
|
||||
assert (c != m_jobData.end ());
|
||||
|
||||
// NIKB: This is ugly and I hate it. We must remove jtINVALID completely
|
||||
// and use something sane.
|
||||
if (c == m_jobData.end ())
|
||||
return m_invalidJobData;
|
||||
|
||||
return c->second;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// Signals the service stopped if the stopped condition is met.
|
||||
//
|
||||
void checkStopped (ScopedLock const& lock)
|
||||
{
|
||||
// We are stopped when all of the following are true:
|
||||
//
|
||||
// 1. A stop notification was received
|
||||
// 2. All Stoppable children have stopped
|
||||
// 3. There are no executing calls to processTask
|
||||
// 4. There are no remaining Jobs in the job set
|
||||
//
|
||||
if (isStopping() &&
|
||||
areChildrenStopped() &&
|
||||
(m_processCount == 0) &&
|
||||
m_jobSet.empty())
|
||||
{
|
||||
stopped();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Signals an added Job for processing.
|
||||
//
|
||||
// Pre-conditions:
|
||||
// The JobType must be valid.
|
||||
// The Job must exist in mJobSet.
|
||||
// The Job must not have previously been queued.
|
||||
//
|
||||
// Post-conditions:
|
||||
// Count of waiting jobs of that type will be incremented.
|
||||
// If JobQueue exists, and has at least one thread, Job will eventually run.
|
||||
//
|
||||
// Invariants:
|
||||
// The calling thread owns the JobLock
|
||||
//
|
||||
void queueJob (Job const& job, ScopedLock const& lock)
|
||||
{
|
||||
JobType const type (job.getType ());
|
||||
assert (type != jtINVALID);
|
||||
assert (m_jobSet.find (job) != m_jobSet.end ());
|
||||
|
||||
JobTypeData& data (getJobTypeData (type));
|
||||
|
||||
if (data.waiting + data.running < getJobLimit (type))
|
||||
{
|
||||
m_workers.addTask ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// defer the task until we go below the limit
|
||||
//
|
||||
++data.deferred;
|
||||
}
|
||||
++data.waiting;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Returns the next Job we should run now.
|
||||
//
|
||||
// RunnableJob:
|
||||
// A Job in the JobSet whose slots count for its type is greater than zero.
|
||||
//
|
||||
// Pre-conditions:
|
||||
// mJobSet must not be empty.
|
||||
// mJobSet holds at least one RunnableJob
|
||||
//
|
||||
// Post-conditions:
|
||||
// job is a valid Job object.
|
||||
// job is removed from mJobQueue.
|
||||
// Waiting job count of it's type is decremented
|
||||
// Running job count of it's type is incremented
|
||||
//
|
||||
// Invariants:
|
||||
// The calling thread owns the JobLock
|
||||
//
|
||||
void getNextJob (Job& job, ScopedLock const& lock)
|
||||
{
|
||||
assert (! m_jobSet.empty ());
|
||||
|
||||
JobSet::const_iterator iter;
|
||||
for (iter = m_jobSet.begin (); iter != m_jobSet.end (); ++iter)
|
||||
{
|
||||
JobTypeData& data (getJobTypeData (iter->getType ()));
|
||||
|
||||
assert (data.running <= getJobLimit (data.type ()));
|
||||
|
||||
// Run this job if we're running below the limit.
|
||||
if (data.running < getJobLimit (data.type ()))
|
||||
{
|
||||
assert (data.waiting > 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert (iter != m_jobSet.end ());
|
||||
|
||||
JobType const type = iter->getType ();
|
||||
JobTypeData& data (getJobTypeData (type));
|
||||
|
||||
assert (type != jtINVALID);
|
||||
|
||||
job = *iter;
|
||||
m_jobSet.erase (iter);
|
||||
|
||||
--data.waiting;
|
||||
++data.running;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Indicates that a running Job has completed its task.
|
||||
//
|
||||
// Pre-conditions:
|
||||
// Job must not exist in mJobSet.
|
||||
// The JobType must not be invalid.
|
||||
//
|
||||
// Post-conditions:
|
||||
// The running count of that JobType is decremented
|
||||
// A new task is signaled if there are more waiting Jobs than the limit, if any.
|
||||
//
|
||||
// Invariants:
|
||||
// <none>
|
||||
//
|
||||
void finishJob (Job const& job, ScopedLock const& lock)
|
||||
{
|
||||
JobType const type = job.getType ();
|
||||
|
||||
assert (m_jobSet.find (job) == m_jobSet.end ());
|
||||
assert (type != jtINVALID);
|
||||
|
||||
JobTypeData& data (getJobTypeData (type));
|
||||
|
||||
// Queue a deferred task if possible
|
||||
if (data.deferred > 0)
|
||||
{
|
||||
assert (data.running + data.waiting >= getJobLimit (type));
|
||||
|
||||
--data.deferred;
|
||||
m_workers.addTask ();
|
||||
}
|
||||
|
||||
--data.running;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
template <class Rep, class Period>
|
||||
void on_dequeue (JobType type,
|
||||
std::chrono::duration <Rep, Period> const& value)
|
||||
{
|
||||
auto const ms (ceil <std::chrono::milliseconds> (value));
|
||||
|
||||
if (ms.count() >= 10)
|
||||
getJobTypeData (type).dequeue.notify (ms);
|
||||
}
|
||||
|
||||
template <class Rep, class Period>
|
||||
void on_execute (JobType type,
|
||||
std::chrono::duration <Rep, Period> const& value)
|
||||
{
|
||||
auto const ms (ceil <std::chrono::milliseconds> (value));
|
||||
|
||||
if (ms.count() >= 10)
|
||||
getJobTypeData (type).execute.notify (ms);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Runs the next appropriate waiting Job.
|
||||
//
|
||||
// Pre-conditions:
|
||||
// A RunnableJob must exist in the JobSet
|
||||
//
|
||||
// Post-conditions:
|
||||
// The chosen RunnableJob will have Job::doJob() called.
|
||||
//
|
||||
// Invariants:
|
||||
// <none>
|
||||
//
|
||||
void processTask ()
|
||||
{
|
||||
Job job;
|
||||
|
||||
{
|
||||
ScopedLock lock (m_mutex);
|
||||
getNextJob (job, lock);
|
||||
++m_processCount;
|
||||
}
|
||||
|
||||
JobTypeData& data (getJobTypeData (job.getType ()));
|
||||
|
||||
// Skip the job if we are stopping and the
|
||||
// skipOnStop flag is set for the job type
|
||||
//
|
||||
if (!isStopping() || !data.info.skip ())
|
||||
{
|
||||
beast::Thread::setCurrentThreadName (data.name ());
|
||||
m_journal.trace << "Doing " << data.name () << " job";
|
||||
|
||||
Job::clock_type::time_point const start_time (
|
||||
Job::clock_type::now());
|
||||
|
||||
on_dequeue (job.getType (), start_time - job.queue_time ());
|
||||
job.doJob ();
|
||||
on_execute (job.getType (), Job::clock_type::now() - start_time);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_journal.trace << "Skipping processTask ('" << data.name () << "')";
|
||||
}
|
||||
|
||||
{
|
||||
ScopedLock lock (m_mutex);
|
||||
finishJob (job, lock);
|
||||
--m_processCount;
|
||||
checkStopped (lock);
|
||||
}
|
||||
|
||||
// Note that when Job::~Job is called, the last reference
|
||||
// to the associated LoadEvent object (in the Job) may be destroyed.
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Returns `true` if all jobs of this type should be skipped when
|
||||
// the JobQueue receives a stop notification. If the job type isn't
|
||||
// skipped, the Job will be called and the job must call Job::shouldCancel
|
||||
// to determine if a long running or non-mandatory operation should be canceled.
|
||||
bool skipOnStop (JobType type)
|
||||
{
|
||||
JobTypeInfo const& j (getJobTypes ().get (type));
|
||||
assert (j.type () != jtINVALID);
|
||||
|
||||
return j.skip ();
|
||||
}
|
||||
|
||||
// Returns the limit of running jobs for the given job type.
|
||||
// For jobs with no limit, we return the largest int. Hopefully that
|
||||
// will be enough.
|
||||
//
|
||||
int getJobLimit (JobType type)
|
||||
{
|
||||
JobTypeInfo const& j (getJobTypes ().get (type));
|
||||
assert (j.type () != jtINVALID);
|
||||
|
||||
return j.limit ();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void onStop ()
|
||||
{
|
||||
// VFALCO NOTE I wanted to remove all the jobs that are skippable
|
||||
// but then the Workers count of tasks to process
|
||||
// goes wrong.
|
||||
|
||||
/*
|
||||
{
|
||||
ScopedLock lock (m_mutex);
|
||||
|
||||
// Remove all jobs whose type is skipOnStop
|
||||
typedef ripple::unordered_map <JobType, std::size_t> JobDataMap;
|
||||
JobDataMap counts;
|
||||
bool const report (m_journal.debug.active());
|
||||
|
||||
for (JobSet::const_iterator iter (m_jobSet.begin());
|
||||
iter != m_jobSet.end();)
|
||||
{
|
||||
if (skipOnStop (iter->getType()))
|
||||
{
|
||||
if (report)
|
||||
{
|
||||
std::pair <JobDataMap::iterator, bool> result (
|
||||
counts.insert (std::make_pair (iter->getType(), 1)));
|
||||
if (! result.second)
|
||||
++(result.first->second);
|
||||
}
|
||||
|
||||
iter = m_jobSet.erase (iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
if (report)
|
||||
{
|
||||
beast::Journal::ScopedStream s (m_journal.debug);
|
||||
|
||||
for (JobDataMap::const_iterator iter (counts.begin());
|
||||
iter != counts.end(); ++iter)
|
||||
{
|
||||
s << std::endl <<
|
||||
"Removed " << iter->second <<
|
||||
" skiponStop jobs of type " << Job::toString (iter->first);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void onChildrenStopped ()
|
||||
{
|
||||
ScopedLock lock (m_mutex);
|
||||
|
||||
checkStopped (lock);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
JobQueue::JobQueue (char const* name, Stoppable& parent)
|
||||
: Stoppable (name, parent)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
std::unique_ptr <JobQueue> make_JobQueue (
|
||||
beast::insight::Collector::ptr const& collector,
|
||||
beast::Stoppable& parent, beast::Journal journal)
|
||||
{
|
||||
return std::make_unique <JobQueueImp> (collector, parent, journal);
|
||||
}
|
||||
|
||||
}
|
||||
80
src/ripple/module/core/functional/JobQueue.h
Normal file
80
src/ripple/module/core/functional/JobQueue.h
Normal file
@@ -0,0 +1,80 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_CORE_JOBQUEUE_H_INCLUDED
|
||||
#define RIPPLE_CORE_JOBQUEUE_H_INCLUDED
|
||||
|
||||
#include <beast/threads/Stoppable.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class JobQueue : public beast::Stoppable
|
||||
{
|
||||
protected:
|
||||
JobQueue (char const* name, Stoppable& parent);
|
||||
|
||||
public:
|
||||
virtual ~JobQueue () { }
|
||||
|
||||
// VFALCO NOTE Using boost::function here because Visual Studio 2012
|
||||
// std::function doesn't swallow return types.
|
||||
//
|
||||
// TODO Replace with std::function
|
||||
//
|
||||
virtual void addJob (JobType type,
|
||||
std::string const& name, boost::function <void (Job&)> const& job) = 0;
|
||||
|
||||
// Jobs waiting at this priority
|
||||
virtual int getJobCount (JobType t) = 0;
|
||||
|
||||
// Jobs waiting plus running at this priority
|
||||
virtual int getJobCountTotal (JobType t) = 0;
|
||||
|
||||
// All waiting jobs at or greater than this priority
|
||||
virtual int getJobCountGE (JobType t) = 0;
|
||||
|
||||
virtual void shutdown () = 0;
|
||||
|
||||
virtual void setThreadCount (int c, bool const standaloneMode) = 0;
|
||||
|
||||
// VFALCO TODO Rename these to newLoadEventMeasurement or something similar
|
||||
// since they create the object.
|
||||
//
|
||||
virtual LoadEvent::pointer getLoadEvent (JobType t, const std::string& name) = 0;
|
||||
|
||||
// VFALCO TODO Why do we need two versions, one which returns a shared
|
||||
// pointer and the other which returns an autoptr?
|
||||
//
|
||||
virtual LoadEvent::autoptr getLoadEventAP (JobType t, const std::string& name) = 0;
|
||||
|
||||
// Add multiple load events
|
||||
virtual void addLoadEvents (JobType t,
|
||||
int count, std::chrono::milliseconds elapsed) = 0;
|
||||
|
||||
virtual bool isOverloaded () = 0;
|
||||
|
||||
virtual Json::Value getJson (int c = 0) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr <JobQueue> make_JobQueue (beast::insight::Collector::ptr const& collector,
|
||||
beast::Stoppable& parent, beast::Journal journal);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
99
src/ripple/module/core/functional/JobTypeData.h
Normal file
99
src/ripple/module/core/functional/JobTypeData.h
Normal file
@@ -0,0 +1,99 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_CORE_JOBTYPEDATA_H_INCLUDED
|
||||
#define RIPPLE_CORE_JOBTYPEDATA_H_INCLUDED
|
||||
|
||||
#include <ripple/module/core/functional/JobTypeInfo.h>
|
||||
|
||||
namespace ripple
|
||||
{
|
||||
|
||||
struct JobTypeData
|
||||
{
|
||||
private:
|
||||
LoadMonitor m_load;
|
||||
|
||||
/* Support for insight */
|
||||
beast::insight::Collector::ptr m_collector;
|
||||
|
||||
public:
|
||||
/* The job category which we represent */
|
||||
JobTypeInfo const& info;
|
||||
|
||||
/* The number of jobs waiting */
|
||||
int waiting;
|
||||
|
||||
/* The number presently running */
|
||||
int running;
|
||||
|
||||
/* And the number we deferred executing because of job limits */
|
||||
int deferred;
|
||||
|
||||
/* Notification callbacks */
|
||||
beast::insight::Event dequeue;
|
||||
beast::insight::Event execute;
|
||||
|
||||
explicit JobTypeData (JobTypeInfo const& info_,
|
||||
beast::insight::Collector::ptr const& collector) noexcept
|
||||
: m_collector (collector)
|
||||
, info (info_)
|
||||
, waiting (0)
|
||||
, running (0)
|
||||
, deferred (0)
|
||||
{
|
||||
m_load.setTargetLatency (
|
||||
info.getAverageLatency (),
|
||||
info.getPeakLatency());
|
||||
|
||||
if (!info.special ())
|
||||
{
|
||||
dequeue = m_collector->make_event (info.name () + "_q");
|
||||
execute = m_collector->make_event (info.name ());
|
||||
}
|
||||
}
|
||||
|
||||
/* Not copy-constructible or assignable */
|
||||
JobTypeData (JobTypeData const& other) = delete;
|
||||
JobTypeData& operator= (JobTypeData const& other) = delete;
|
||||
|
||||
std::string name () const
|
||||
{
|
||||
return info.name ();
|
||||
}
|
||||
|
||||
JobType type () const
|
||||
{
|
||||
return info.type ();
|
||||
}
|
||||
|
||||
LoadMonitor& load ()
|
||||
{
|
||||
return m_load;
|
||||
}
|
||||
|
||||
LoadMonitor::Stats stats ()
|
||||
{
|
||||
return m_load.getStats ();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
101
src/ripple/module/core/functional/JobTypeInfo.h
Normal file
101
src/ripple/module/core/functional/JobTypeInfo.h
Normal file
@@ -0,0 +1,101 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_CORE_JOBTYPEINFO_H_INCLUDED
|
||||
#define RIPPLE_CORE_JOBTYPEINFO_H_INCLUDED
|
||||
|
||||
namespace ripple
|
||||
{
|
||||
|
||||
/** Holds all the 'static' information about a job, which does not change */
|
||||
class JobTypeInfo
|
||||
{
|
||||
private:
|
||||
JobType const m_type;
|
||||
std::string const m_name;
|
||||
|
||||
/** The limit on the number of running jobs for this job type. */
|
||||
int const m_limit;
|
||||
|
||||
/** Can be skipped */
|
||||
bool const m_skip;
|
||||
|
||||
/** Special jobs are not dispatched via the job queue */
|
||||
bool const m_special;
|
||||
|
||||
/** Average and peak latencies for this job type. 0 is none specified */
|
||||
std::uint64_t const m_avgLatency;
|
||||
std::uint64_t const m_peakLatency;
|
||||
|
||||
public:
|
||||
// Not default constructible
|
||||
JobTypeInfo () = delete;
|
||||
|
||||
JobTypeInfo (JobType type, std::string name, int limit,
|
||||
bool skip, bool special, std::uint64_t avgLatency, std::uint64_t peakLatency)
|
||||
: m_type (type)
|
||||
, m_name (name)
|
||||
, m_limit (limit)
|
||||
, m_skip (skip)
|
||||
, m_special (special)
|
||||
, m_avgLatency (avgLatency)
|
||||
, m_peakLatency (peakLatency)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
JobType type () const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
std::string name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
int limit () const
|
||||
{
|
||||
return m_limit;
|
||||
}
|
||||
|
||||
bool skip () const
|
||||
{
|
||||
return m_skip;
|
||||
}
|
||||
|
||||
bool special () const
|
||||
{
|
||||
return m_special;
|
||||
}
|
||||
|
||||
std::uint64_t getAverageLatency () const
|
||||
{
|
||||
return m_avgLatency;
|
||||
}
|
||||
|
||||
std::uint64_t getPeakLatency () const
|
||||
{
|
||||
return m_peakLatency;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
226
src/ripple/module/core/functional/JobTypes.h
Normal file
226
src/ripple/module/core/functional/JobTypes.h
Normal file
@@ -0,0 +1,226 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/module/core/functional/Job.h>
|
||||
#include <ripple/module/core/functional/JobTypeInfo.h>
|
||||
|
||||
namespace ripple
|
||||
{
|
||||
|
||||
class JobTypes
|
||||
{
|
||||
public:
|
||||
typedef std::map <JobType, JobTypeInfo> Map;
|
||||
typedef Map::const_iterator const_iterator;
|
||||
|
||||
|
||||
JobTypes ()
|
||||
: m_unknown (jtINVALID, "invalid", 0, true, true, 0, 0)
|
||||
{
|
||||
int maxLimit = std::numeric_limits <int>::max ();
|
||||
|
||||
// Make a fetch pack for a peer
|
||||
add (jtPACK, "makeFetchPack",
|
||||
1, true, false, 0, 0);
|
||||
|
||||
// An old ledger has been accepted
|
||||
add (jtPUBOLDLEDGER, "publishAcqLedger",
|
||||
2, true, false, 10000, 15000);
|
||||
|
||||
// A validation from an untrusted source
|
||||
add (jtVALIDATION_ut, "untrustedValidation",
|
||||
maxLimit, true, false, 2000, 5000);
|
||||
|
||||
// A proof of work demand from another server
|
||||
add (jtPROOFWORK, "proofOfWork",
|
||||
maxLimit, true, false, 2000, 5000);
|
||||
|
||||
// A local transaction
|
||||
add (jtTRANSACTION_l, "localTransaction",
|
||||
maxLimit, true, false, 100, 500);
|
||||
|
||||
// A proposal from an untrusted source
|
||||
add (jtPROPOSAL_ut, "untrustedProposal",
|
||||
maxLimit, true, false, 500, 1250);
|
||||
|
||||
// Received data for a ledger we're acquiring
|
||||
add (jtLEDGER_DATA, "ledgerData",
|
||||
2, true, false, 0, 0);
|
||||
|
||||
// Update pathfinding requests
|
||||
add (jtUPDATE_PF, "updatePaths",
|
||||
maxLimit, true, false, 0, 0);
|
||||
|
||||
// A websocket command from the client
|
||||
add (jtCLIENT, "clientCommand",
|
||||
maxLimit, true, false, 2000, 5000);
|
||||
|
||||
// A websocket command from the client
|
||||
add (jtRPC, "RPC",
|
||||
maxLimit, false, false, 0, 0);
|
||||
|
||||
// A transaction received from the network
|
||||
add (jtTRANSACTION, "transaction",
|
||||
maxLimit, true, false, 250, 1000);
|
||||
|
||||
// A Score or Fetch of the UNL (DEPRECATED)
|
||||
add (jtUNL, "unl",
|
||||
1, true, false, 0, 0);
|
||||
|
||||
// Advance validated/acquired ledgers
|
||||
add (jtADVANCE, "advanceLedger",
|
||||
maxLimit, true, false, 0, 0);
|
||||
|
||||
// Publish a fully-accepted ledger
|
||||
add (jtPUBLEDGER, "publishNewLedger",
|
||||
maxLimit, true, false, 3000, 4500);
|
||||
|
||||
// Fetch a proposed set
|
||||
add (jtTXN_DATA, "fetchTxnData",
|
||||
1, true, false, 0, 0);
|
||||
|
||||
// Write-ahead logging
|
||||
add (jtWAL, "writeAhead",
|
||||
maxLimit, false, false, 1000, 2500);
|
||||
|
||||
// A validation from a trusted source
|
||||
add (jtVALIDATION_t, "trustedValidation",
|
||||
maxLimit, true, false, 500, 1500);
|
||||
|
||||
// Write out hashed objects
|
||||
add (jtWRITE, "writeObjects",
|
||||
maxLimit, false, false, 1750, 2500);
|
||||
|
||||
// Accept a consensus ledger
|
||||
add (jtACCEPT, "acceptLedger",
|
||||
maxLimit, false, false, 0, 0);
|
||||
|
||||
// A proposal from a trusted source
|
||||
add (jtPROPOSAL_t, "trustedProposal",
|
||||
maxLimit, false, false, 100, 500);
|
||||
|
||||
// Sweep for stale structures
|
||||
add (jtSWEEP, "sweep",
|
||||
maxLimit, true, false, 0, 0);
|
||||
|
||||
// NetworkOPs cluster peer report
|
||||
add (jtNETOP_CLUSTER, "clusterReport",
|
||||
1, true, false, 9999, 9999);
|
||||
|
||||
// NetworkOPs net timer processing
|
||||
add (jtNETOP_TIMER, "heartbeat",
|
||||
1, true, false, 999, 999);
|
||||
|
||||
// An administrative operation
|
||||
add (jtADMIN, "administration",
|
||||
maxLimit, true, false, 0, 0);
|
||||
|
||||
// The rest are special job types that are not dispatched
|
||||
// by the job pool. The "limit" and "skip" attributes are
|
||||
// not applicable to these types of jobs.
|
||||
|
||||
add (jtPEER, "peerCommand",
|
||||
0, false, true, 200, 2500);
|
||||
|
||||
add (jtDISK, "diskAccess",
|
||||
0, false, true, 500, 1000);
|
||||
|
||||
add (jtTXN_PROC, "processTransaction",
|
||||
0, false, true, 0, 0);
|
||||
|
||||
add (jtOB_SETUP, "orderBookSetup",
|
||||
0, false, true, 0, 0);
|
||||
|
||||
add (jtPATH_FIND, "pathFind",
|
||||
0, false, true, 0, 0);
|
||||
|
||||
add (jtHO_READ, "nodeRead",
|
||||
0, false, true, 0, 0);
|
||||
|
||||
add (jtHO_WRITE, "nodeWrite",
|
||||
0, false, true, 0, 0);
|
||||
|
||||
add (jtGENERIC, "generic",
|
||||
0, false, true, 0, 0);
|
||||
|
||||
add (jtNS_SYNC_READ, "SyncReadNode",
|
||||
0, false, true, 0, 0);
|
||||
add (jtNS_ASYNC_READ, "AsyncReadNode",
|
||||
0, false, true, 0, 0);
|
||||
add (jtNS_WRITE, "WriteNode",
|
||||
0, false, true, 0, 0);
|
||||
|
||||
}
|
||||
|
||||
JobTypeInfo const& get (JobType jt) const
|
||||
{
|
||||
Map::const_iterator const iter (m_map.find (jt));
|
||||
assert (iter != m_map.end ());
|
||||
|
||||
if (iter != m_map.end())
|
||||
return iter->second;
|
||||
|
||||
return m_unknown;
|
||||
}
|
||||
|
||||
JobTypeInfo const& getInvalid () const
|
||||
{
|
||||
return m_unknown;
|
||||
}
|
||||
|
||||
const_iterator begin () const
|
||||
{
|
||||
return m_map.cbegin ();
|
||||
}
|
||||
|
||||
const_iterator cbegin () const
|
||||
{
|
||||
return m_map.cbegin ();
|
||||
}
|
||||
|
||||
const_iterator end () const
|
||||
{
|
||||
return m_map.cend ();
|
||||
}
|
||||
|
||||
const_iterator cend () const
|
||||
{
|
||||
return m_map.cend ();
|
||||
}
|
||||
|
||||
private:
|
||||
void add(JobType jt, std::string name, int limit,
|
||||
bool skip, bool special, std::uint64_t avgLatency, std::uint64_t peakLatency)
|
||||
{
|
||||
assert (m_map.find (jt) == m_map.end ());
|
||||
|
||||
std::pair<Map::iterator,bool> result (m_map.emplace (
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple (jt),
|
||||
std::forward_as_tuple (jt, name, limit, skip, special,
|
||||
avgLatency, peakLatency)));
|
||||
|
||||
assert (result.second == true);
|
||||
}
|
||||
|
||||
JobTypeInfo m_unknown;
|
||||
Map m_map;
|
||||
};
|
||||
|
||||
}
|
||||
94
src/ripple/module/core/functional/LoadEvent.cpp
Normal file
94
src/ripple/module/core/functional/LoadEvent.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
namespace ripple {
|
||||
|
||||
LoadEvent::LoadEvent (LoadMonitor& monitor, const std::string& name, bool shouldStart)
|
||||
: m_loadMonitor (monitor)
|
||||
, m_isRunning (false)
|
||||
, m_name (name)
|
||||
, m_timeStopped (beast::RelativeTime::fromStartup())
|
||||
, m_secondsWaiting (0)
|
||||
, m_secondsRunning (0)
|
||||
{
|
||||
if (shouldStart)
|
||||
start ();
|
||||
}
|
||||
|
||||
LoadEvent::~LoadEvent ()
|
||||
{
|
||||
if (m_isRunning)
|
||||
stop ();
|
||||
}
|
||||
|
||||
std::string const& LoadEvent::name () const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
double LoadEvent::getSecondsWaiting() const
|
||||
{
|
||||
return m_secondsWaiting;
|
||||
}
|
||||
|
||||
double LoadEvent::getSecondsRunning() const
|
||||
{
|
||||
return m_secondsRunning;
|
||||
}
|
||||
|
||||
double LoadEvent::getSecondsTotal() const
|
||||
{
|
||||
return m_secondsWaiting + m_secondsRunning;
|
||||
}
|
||||
|
||||
void LoadEvent::reName (const std::string& name)
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
void LoadEvent::start ()
|
||||
{
|
||||
beast::RelativeTime const currentTime (beast::RelativeTime::fromStartup());
|
||||
|
||||
// If we already called start, this call will replace the previous one.
|
||||
if (m_isRunning)
|
||||
{
|
||||
m_secondsWaiting += (currentTime - m_timeStarted).inSeconds();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_secondsWaiting += (currentTime - m_timeStopped).inSeconds();
|
||||
m_isRunning = true;
|
||||
}
|
||||
|
||||
m_timeStarted = currentTime;
|
||||
}
|
||||
|
||||
void LoadEvent::stop ()
|
||||
{
|
||||
bassert (m_isRunning);
|
||||
|
||||
m_timeStopped = beast::RelativeTime::fromStartup();
|
||||
m_secondsRunning += (m_timeStopped - m_timeStarted).inSeconds();
|
||||
|
||||
m_isRunning = false;
|
||||
m_loadMonitor.addLoadSample (*this);
|
||||
}
|
||||
|
||||
} // ripple
|
||||
86
src/ripple/module/core/functional/LoadEvent.h
Normal file
86
src/ripple/module/core/functional/LoadEvent.h
Normal file
@@ -0,0 +1,86 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_LOADEVENT_H
|
||||
#define RIPPLE_LOADEVENT_H
|
||||
|
||||
#include <beast/chrono/RelativeTime.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class LoadMonitor;
|
||||
|
||||
// VFALCO NOTE What is the difference between a LoadEvent and a LoadMonitor?
|
||||
// VFALCO TODO Rename LoadEvent to ScopedLoadSample
|
||||
//
|
||||
// This looks like a scoped elapsed time measuring class
|
||||
//
|
||||
class LoadEvent
|
||||
{
|
||||
public:
|
||||
// VFALCO NOTE Why are these shared pointers? Wouldn't there be a
|
||||
// piece of lifetime-managed calling code that can simply own
|
||||
// the object?
|
||||
//
|
||||
// Why both kinds of containers?
|
||||
//
|
||||
typedef boost::shared_ptr <LoadEvent> pointer;
|
||||
typedef std::unique_ptr <LoadEvent> autoptr;
|
||||
|
||||
public:
|
||||
// VFALCO TODO remove the dependency on LoadMonitor. Is that possible?
|
||||
LoadEvent (LoadMonitor& monitor,
|
||||
const std::string& name,
|
||||
bool shouldStart);
|
||||
|
||||
~LoadEvent ();
|
||||
|
||||
std::string const& name () const;
|
||||
double getSecondsWaiting() const;
|
||||
double getSecondsRunning() const;
|
||||
double getSecondsTotal() const;
|
||||
|
||||
// VFALCO TODO rename this to setName () or setLabel ()
|
||||
void reName (const std::string& name);
|
||||
|
||||
// Start the measurement. The constructor calls this automatically if
|
||||
// shouldStart is true. If the operation is aborted, start() can be
|
||||
// called again later.
|
||||
//
|
||||
void start ();
|
||||
|
||||
// Stops the measurement and reports the results. The time reported is
|
||||
// measured from the last call to start.
|
||||
//
|
||||
void stop ();
|
||||
|
||||
private:
|
||||
LoadMonitor& m_loadMonitor;
|
||||
bool m_isRunning;
|
||||
std::string m_name;
|
||||
// VFALCO TODO Replace these with chrono
|
||||
beast::RelativeTime m_timeStopped;
|
||||
beast::RelativeTime m_timeStarted;
|
||||
double m_secondsWaiting;
|
||||
double m_secondsRunning;
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
73
src/ripple/module/core/functional/LoadFeeTrack.h
Normal file
73
src/ripple/module/core/functional/LoadFeeTrack.h
Normal file
@@ -0,0 +1,73 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_LOADFEETRACK_H_INCLUDED
|
||||
#define RIPPLE_LOADFEETRACK_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/** Manages the current fee schedule.
|
||||
|
||||
The "base" fee is the cost to send a reference transaction under no load,
|
||||
expressed in millionths of one XRP.
|
||||
|
||||
The "load" fee is how much the local server currently charges to send a
|
||||
reference transaction. This fee fluctuates based on the load of the
|
||||
server.
|
||||
*/
|
||||
// VFALCO TODO Rename "load" to "current".
|
||||
class LoadFeeTrack
|
||||
{
|
||||
public:
|
||||
/** Create a new tracker.
|
||||
*/
|
||||
static LoadFeeTrack* New (beast::Journal journal);
|
||||
|
||||
virtual ~LoadFeeTrack () { }
|
||||
|
||||
// Scale from fee units to millionths of a ripple
|
||||
virtual std::uint64_t scaleFeeBase (std::uint64_t fee, std::uint64_t baseFee,
|
||||
std::uint32_t referenceFeeUnits) = 0;
|
||||
|
||||
// Scale using load as well as base rate
|
||||
virtual std::uint64_t scaleFeeLoad (std::uint64_t fee, std::uint64_t baseFee,
|
||||
std::uint32_t referenceFeeUnits,
|
||||
bool bAdmin) = 0;
|
||||
|
||||
virtual void setRemoteFee (std::uint32_t) = 0;
|
||||
|
||||
virtual std::uint32_t getRemoteFee () = 0;
|
||||
virtual std::uint32_t getLocalFee () = 0;
|
||||
virtual std::uint32_t getClusterFee () = 0;
|
||||
|
||||
virtual std::uint32_t getLoadBase () = 0;
|
||||
virtual std::uint32_t getLoadFactor () = 0;
|
||||
|
||||
virtual Json::Value getJson (std::uint64_t baseFee, std::uint32_t referenceFeeUnits) = 0;
|
||||
|
||||
virtual void setClusterFee (std::uint32_t) = 0;
|
||||
virtual bool raiseLocalFee () = 0;
|
||||
virtual bool lowerLocalFee () = 0;
|
||||
virtual bool isLoadedLocal () = 0;
|
||||
virtual bool isLoadedCluster () = 0;
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
56
src/ripple/module/core/functional/LoadFeeTrackImp.cpp
Normal file
56
src/ripple/module/core/functional/LoadFeeTrackImp.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <beast/unit_test/suite.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
LoadFeeTrack* LoadFeeTrack::New (beast::Journal journal)
|
||||
{
|
||||
return new LoadFeeTrackImp (journal);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class LoadFeeTrack_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void run ()
|
||||
{
|
||||
Config d; // get a default configuration object
|
||||
LoadFeeTrackImp l;
|
||||
|
||||
expect (l.scaleFeeBase (10000, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 10000);
|
||||
expect (l.scaleFeeLoad (10000, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE, false) == 10000);
|
||||
expect (l.scaleFeeBase (1, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 1);
|
||||
expect (l.scaleFeeLoad (1, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE, false) == 1);
|
||||
|
||||
// Check new default fee values give same fees as old defaults
|
||||
expect (l.scaleFeeBase (d.FEE_DEFAULT, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 10);
|
||||
expect (l.scaleFeeBase (d.FEE_ACCOUNT_RESERVE, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 200 * SYSTEM_CURRENCY_PARTS);
|
||||
expect (l.scaleFeeBase (d.FEE_OWNER_RESERVE, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 50 * SYSTEM_CURRENCY_PARTS);
|
||||
expect (l.scaleFeeBase (d.FEE_NICKNAME_CREATE, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 1000);
|
||||
expect (l.scaleFeeBase (d.FEE_OFFER, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 10);
|
||||
expect (l.scaleFeeBase (d.FEE_CONTRACT_OPERATION, d.FEE_DEFAULT, d.TRANSACTION_FEE_BASE) == 1);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(LoadFeeTrack,ripple_core,ripple);
|
||||
|
||||
} // ripple
|
||||
236
src/ripple/module/core/functional/LoadFeeTrackImp.h
Normal file
236
src/ripple/module/core/functional/LoadFeeTrackImp.h
Normal file
@@ -0,0 +1,236 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_LOADFEETRACKIMP_H_INCLUDED
|
||||
#define RIPPLE_LOADFEETRACKIMP_H_INCLUDED
|
||||
|
||||
#include <ripple/common/jsonrpc_fields.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class LoadFeeTrackImp : public LoadFeeTrack
|
||||
{
|
||||
public:
|
||||
explicit LoadFeeTrackImp (beast::Journal journal = beast::Journal())
|
||||
: m_journal (journal)
|
||||
, mLocalTxnLoadFee (lftNormalFee)
|
||||
, mRemoteTxnLoadFee (lftNormalFee)
|
||||
, mClusterTxnLoadFee (lftNormalFee)
|
||||
, raiseCount (0)
|
||||
{
|
||||
}
|
||||
|
||||
// Scale using load as well as base rate
|
||||
std::uint64_t scaleFeeLoad (std::uint64_t fee, std::uint64_t baseFee, std::uint32_t referenceFeeUnits, bool bAdmin)
|
||||
{
|
||||
static std::uint64_t midrange (0x00000000FFFFFFFF);
|
||||
|
||||
bool big = (fee > midrange);
|
||||
|
||||
if (big) // big fee, divide first to avoid overflow
|
||||
fee /= baseFee;
|
||||
else // normal fee, multiply first for accuracy
|
||||
fee *= referenceFeeUnits;
|
||||
|
||||
std::uint32_t feeFactor = std::max (mLocalTxnLoadFee, mRemoteTxnLoadFee);
|
||||
|
||||
// Let admins pay the normal fee until the local load exceeds four times the remote
|
||||
std::uint32_t uRemFee = std::max(mRemoteTxnLoadFee, mClusterTxnLoadFee);
|
||||
if (bAdmin && (feeFactor > uRemFee) && (feeFactor < (4 * uRemFee)))
|
||||
feeFactor = uRemFee;
|
||||
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
fee = mulDiv (fee, feeFactor, lftNormalFee);
|
||||
}
|
||||
|
||||
if (big) // Fee was big to start, must now multiply
|
||||
fee *= referenceFeeUnits;
|
||||
else // Fee was small to start, mst now divide
|
||||
fee /= baseFee;
|
||||
|
||||
return fee;
|
||||
}
|
||||
|
||||
// Scale from fee units to millionths of a ripple
|
||||
std::uint64_t scaleFeeBase (std::uint64_t fee, std::uint64_t baseFee, std::uint32_t referenceFeeUnits)
|
||||
{
|
||||
return mulDiv (fee, referenceFeeUnits, baseFee);
|
||||
}
|
||||
|
||||
std::uint32_t getRemoteFee ()
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
return mRemoteTxnLoadFee;
|
||||
}
|
||||
|
||||
std::uint32_t getLocalFee ()
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
return mLocalTxnLoadFee;
|
||||
}
|
||||
|
||||
std::uint32_t getLoadBase ()
|
||||
{
|
||||
return lftNormalFee;
|
||||
}
|
||||
|
||||
std::uint32_t getLoadFactor ()
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
return std::max(mClusterTxnLoadFee, std::max (mLocalTxnLoadFee, mRemoteTxnLoadFee));
|
||||
}
|
||||
|
||||
void setClusterFee (std::uint32_t fee)
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
mClusterTxnLoadFee = fee;
|
||||
}
|
||||
|
||||
std::uint32_t getClusterFee ()
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
return mClusterTxnLoadFee;
|
||||
}
|
||||
|
||||
bool isLoadedLocal ()
|
||||
{
|
||||
// VFALCO TODO This could be replaced with a SharedData and
|
||||
// using a read/write lock instead of a critical section.
|
||||
//
|
||||
// NOTE This applies to all the locking in this class.
|
||||
//
|
||||
//
|
||||
ScopedLockType sl (mLock);
|
||||
return (raiseCount != 0) || (mLocalTxnLoadFee != lftNormalFee);
|
||||
}
|
||||
|
||||
bool isLoadedCluster ()
|
||||
{
|
||||
// VFALCO TODO This could be replaced with a SharedData and
|
||||
// using a read/write lock instead of a critical section.
|
||||
//
|
||||
// NOTE This applies to all the locking in this class.
|
||||
//
|
||||
//
|
||||
ScopedLockType sl (mLock);
|
||||
return (raiseCount != 0) || (mLocalTxnLoadFee != lftNormalFee) || (mClusterTxnLoadFee != lftNormalFee);
|
||||
}
|
||||
|
||||
void setRemoteFee (std::uint32_t f)
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
mRemoteTxnLoadFee = f;
|
||||
}
|
||||
|
||||
bool raiseLocalFee ()
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
|
||||
if (++raiseCount < 2)
|
||||
return false;
|
||||
|
||||
std::uint32_t origFee = mLocalTxnLoadFee;
|
||||
|
||||
if (mLocalTxnLoadFee < mRemoteTxnLoadFee) // make sure this fee takes effect
|
||||
mLocalTxnLoadFee = mRemoteTxnLoadFee;
|
||||
|
||||
mLocalTxnLoadFee += (mLocalTxnLoadFee / lftFeeIncFraction); // increment by 1/16th
|
||||
|
||||
if (mLocalTxnLoadFee > lftFeeMax)
|
||||
mLocalTxnLoadFee = lftFeeMax;
|
||||
|
||||
if (origFee == mLocalTxnLoadFee)
|
||||
return false;
|
||||
|
||||
m_journal.debug << "Local load fee raised from " << origFee << " to " << mLocalTxnLoadFee;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lowerLocalFee ()
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
std::uint32_t origFee = mLocalTxnLoadFee;
|
||||
raiseCount = 0;
|
||||
|
||||
mLocalTxnLoadFee -= (mLocalTxnLoadFee / lftFeeDecFraction ); // reduce by 1/4
|
||||
|
||||
if (mLocalTxnLoadFee < lftNormalFee)
|
||||
mLocalTxnLoadFee = lftNormalFee;
|
||||
|
||||
if (origFee == mLocalTxnLoadFee)
|
||||
return false;
|
||||
|
||||
m_journal.debug << "Local load fee lowered from " << origFee << " to " << mLocalTxnLoadFee;
|
||||
return true;
|
||||
}
|
||||
|
||||
Json::Value getJson (std::uint64_t baseFee, std::uint32_t referenceFeeUnits)
|
||||
{
|
||||
Json::Value j (Json::objectValue);
|
||||
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
|
||||
// base_fee = The cost to send a "reference" transaction under no load, in millionths of a Ripple
|
||||
j[jss::base_fee] = Json::Value::UInt (baseFee);
|
||||
|
||||
// load_fee = The cost to send a "reference" transaction now, in millionths of a Ripple
|
||||
j[jss::load_fee] = Json::Value::UInt (
|
||||
mulDiv (baseFee, std::max (mLocalTxnLoadFee, mRemoteTxnLoadFee), lftNormalFee));
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
private:
|
||||
// VFALCO TODO Move this function to some "math utilities" file
|
||||
// compute (value)*(mul)/(div) - avoid overflow but keep precision
|
||||
std::uint64_t mulDiv (std::uint64_t value, std::uint32_t mul, std::uint64_t div)
|
||||
{
|
||||
// VFALCO TODO replace with beast::literal64bitUnsigned ()
|
||||
//
|
||||
static std::uint64_t boundary = (0x00000000FFFFFFFF);
|
||||
|
||||
if (value > boundary) // Large value, avoid overflow
|
||||
return (value / div) * mul;
|
||||
else // Normal value, preserve accuracy
|
||||
return (value * mul) / div;
|
||||
}
|
||||
|
||||
private:
|
||||
static const int lftNormalFee = 256; // 256 is the minimum/normal load factor
|
||||
static const int lftFeeIncFraction = 4; // increase fee by 1/4
|
||||
static const int lftFeeDecFraction = 4; // decrease fee by 1/4
|
||||
static const int lftFeeMax = lftNormalFee * 1000000;
|
||||
|
||||
beast::Journal m_journal;
|
||||
typedef RippleMutex LockType;
|
||||
typedef std::lock_guard <LockType> ScopedLockType;
|
||||
LockType mLock;
|
||||
|
||||
std::uint32_t mLocalTxnLoadFee; // Scale factor, lftNormalFee = normal fee
|
||||
std::uint32_t mRemoteTxnLoadFee; // Scale factor, lftNormalFee = normal fee
|
||||
std::uint32_t mClusterTxnLoadFee; // Scale factor, lftNormalFee = normal fee
|
||||
int raiseCount;
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
241
src/ripple/module/core/functional/LoadMonitor.cpp
Normal file
241
src/ripple/module/core/functional/LoadMonitor.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/*
|
||||
|
||||
TODO
|
||||
----
|
||||
|
||||
- Use Journal for logging
|
||||
|
||||
*/
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
LoadMonitor::Stats::Stats()
|
||||
: count (0)
|
||||
, latencyAvg (0)
|
||||
, latencyPeak (0)
|
||||
, isOverloaded (false)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
SETUP_LOG (LoadMonitor)
|
||||
|
||||
LoadMonitor::LoadMonitor ()
|
||||
: mCounts (0)
|
||||
, mLatencyEvents (0)
|
||||
, mLatencyMSAvg (0)
|
||||
, mLatencyMSPeak (0)
|
||||
, mTargetLatencyAvg (0)
|
||||
, mTargetLatencyPk (0)
|
||||
, mLastUpdate (UptimeTimer::getInstance ().getElapsedSeconds ())
|
||||
{
|
||||
}
|
||||
|
||||
// VFALCO NOTE WHY do we need "the mutex?" This dependence on
|
||||
// a hidden global, especially a synchronization primitive,
|
||||
// is a flawed design.
|
||||
// It's not clear exactly which data needs to be protected.
|
||||
//
|
||||
// call with the mutex
|
||||
void LoadMonitor::update ()
|
||||
{
|
||||
int now = UptimeTimer::getInstance ().getElapsedSeconds ();
|
||||
|
||||
// VFALCO TODO stop returning from the middle of functions.
|
||||
|
||||
if (now == mLastUpdate) // current
|
||||
return;
|
||||
|
||||
// VFALCO TODO Why 8?
|
||||
if ((now < mLastUpdate) || (now > (mLastUpdate + 8)))
|
||||
{
|
||||
// way out of date
|
||||
mCounts = 0;
|
||||
mLatencyEvents = 0;
|
||||
mLatencyMSAvg = 0;
|
||||
mLatencyMSPeak = 0;
|
||||
mLastUpdate = now;
|
||||
// VFALCO TODO don't return from the middle...
|
||||
return;
|
||||
}
|
||||
|
||||
// do exponential decay
|
||||
/*
|
||||
David:
|
||||
|
||||
"Imagine if you add 10 to something every second. And you
|
||||
also reduce it by 1/4 every second. It will "idle" at 40,
|
||||
correponding to 10 counts per second."
|
||||
*/
|
||||
do
|
||||
{
|
||||
++mLastUpdate;
|
||||
mCounts -= ((mCounts + 3) / 4);
|
||||
mLatencyEvents -= ((mLatencyEvents + 3) / 4);
|
||||
mLatencyMSAvg -= (mLatencyMSAvg / 4);
|
||||
mLatencyMSPeak -= (mLatencyMSPeak / 4);
|
||||
}
|
||||
while (mLastUpdate < now);
|
||||
}
|
||||
|
||||
void LoadMonitor::addCount ()
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
|
||||
update ();
|
||||
++mCounts;
|
||||
}
|
||||
|
||||
void LoadMonitor::addLatency (int latency)
|
||||
{
|
||||
// VFALCO NOTE Why does 1 become 0?
|
||||
if (latency == 1)
|
||||
latency = 0;
|
||||
|
||||
ScopedLockType sl (mLock);
|
||||
|
||||
update ();
|
||||
|
||||
++mLatencyEvents;
|
||||
mLatencyMSAvg += latency;
|
||||
mLatencyMSPeak += latency;
|
||||
|
||||
// Units are quarters of a millisecond
|
||||
int const latencyPeak = mLatencyEvents * latency * 4;
|
||||
|
||||
if (mLatencyMSPeak < latencyPeak)
|
||||
mLatencyMSPeak = latencyPeak;
|
||||
}
|
||||
|
||||
std::string LoadMonitor::printElapsed (double seconds)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << (std::size_t (seconds * 1000 + 0.5)) << " ms";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void LoadMonitor::addLoadSample (LoadEvent const& sample)
|
||||
{
|
||||
std::string const& name (sample.name());
|
||||
beast::RelativeTime const latency (sample.getSecondsTotal());
|
||||
|
||||
if (latency.inSeconds() > 0.5)
|
||||
{
|
||||
WriteLog ((latency.inSeconds() > 1.0) ? lsWARNING : lsINFO, LoadMonitor)
|
||||
<< "Job: " << name << " ExecutionTime: " << printElapsed (sample.getSecondsRunning()) <<
|
||||
" WaitingTime: " << printElapsed (sample.getSecondsWaiting());
|
||||
}
|
||||
|
||||
// VFALCO NOTE Why does 1 become 0?
|
||||
std::size_t latencyMilliseconds (latency.inMilliseconds());
|
||||
if (latencyMilliseconds == 1)
|
||||
latencyMilliseconds = 0;
|
||||
|
||||
ScopedLockType sl (mLock);
|
||||
|
||||
update ();
|
||||
++mCounts;
|
||||
++mLatencyEvents;
|
||||
mLatencyMSAvg += latencyMilliseconds;
|
||||
mLatencyMSPeak += latencyMilliseconds;
|
||||
|
||||
// VFALCO NOTE Why are we multiplying by 4?
|
||||
int const latencyPeak = mLatencyEvents * latencyMilliseconds * 4;
|
||||
|
||||
if (mLatencyMSPeak < latencyPeak)
|
||||
mLatencyMSPeak = latencyPeak;
|
||||
}
|
||||
|
||||
/* Add multiple samples
|
||||
@param count The number of samples to add
|
||||
@param latencyMS The total number of milliseconds
|
||||
*/
|
||||
void LoadMonitor::addSamples (int count, std::chrono::milliseconds latency)
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
|
||||
update ();
|
||||
mCounts += count;
|
||||
mLatencyEvents += count;
|
||||
mLatencyMSAvg += latency.count();
|
||||
mLatencyMSPeak += latency.count();
|
||||
|
||||
int const latencyPeak = mLatencyEvents * latency.count() * 4 / count;
|
||||
|
||||
if (mLatencyMSPeak < latencyPeak)
|
||||
mLatencyMSPeak = latencyPeak;
|
||||
}
|
||||
|
||||
void LoadMonitor::setTargetLatency (std::uint64_t avg, std::uint64_t pk)
|
||||
{
|
||||
mTargetLatencyAvg = avg;
|
||||
mTargetLatencyPk = pk;
|
||||
}
|
||||
|
||||
bool LoadMonitor::isOverTarget (std::uint64_t avg, std::uint64_t peak)
|
||||
{
|
||||
return (mTargetLatencyPk && (peak > mTargetLatencyPk)) ||
|
||||
(mTargetLatencyAvg && (avg > mTargetLatencyAvg));
|
||||
}
|
||||
|
||||
bool LoadMonitor::isOver ()
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
|
||||
update ();
|
||||
|
||||
if (mLatencyEvents == 0)
|
||||
return 0;
|
||||
|
||||
return isOverTarget (mLatencyMSAvg / (mLatencyEvents * 4), mLatencyMSPeak / (mLatencyEvents * 4));
|
||||
}
|
||||
|
||||
LoadMonitor::Stats LoadMonitor::getStats ()
|
||||
{
|
||||
Stats stats;
|
||||
|
||||
ScopedLockType sl (mLock);
|
||||
|
||||
update ();
|
||||
|
||||
stats.count = mCounts / 4;
|
||||
|
||||
if (mLatencyEvents == 0)
|
||||
{
|
||||
stats.latencyAvg = 0;
|
||||
stats.latencyPeak = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
stats.latencyAvg = mLatencyMSAvg / (mLatencyEvents * 4);
|
||||
stats.latencyPeak = mLatencyMSPeak / (mLatencyEvents * 4);
|
||||
}
|
||||
|
||||
stats.isOverloaded = isOverTarget (stats.latencyAvg, stats.latencyPeak);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
81
src/ripple/module/core/functional/LoadMonitor.h
Normal file
81
src/ripple/module/core/functional/LoadMonitor.h
Normal file
@@ -0,0 +1,81 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_LOADMONITOR_H_INCLUDED
|
||||
#define RIPPLE_LOADMONITOR_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Monitors load levels and response times
|
||||
|
||||
// VFALCO TODO Rename this. Having both LoadManager and LoadMonitor is confusing.
|
||||
//
|
||||
class LoadMonitor
|
||||
{
|
||||
public:
|
||||
LoadMonitor ();
|
||||
|
||||
void addCount ();
|
||||
|
||||
void addLatency (int latency);
|
||||
|
||||
void addLoadSample (LoadEvent const& sample);
|
||||
|
||||
void addSamples (int count, std::chrono::milliseconds latency);
|
||||
|
||||
void setTargetLatency (std::uint64_t avg, std::uint64_t pk);
|
||||
|
||||
bool isOverTarget (std::uint64_t avg, std::uint64_t peak);
|
||||
|
||||
// VFALCO TODO make this return the values in a struct.
|
||||
struct Stats
|
||||
{
|
||||
Stats();
|
||||
|
||||
std::uint64_t count;
|
||||
std::uint64_t latencyAvg;
|
||||
std::uint64_t latencyPeak;
|
||||
bool isOverloaded;
|
||||
};
|
||||
|
||||
Stats getStats ();
|
||||
|
||||
bool isOver ();
|
||||
|
||||
private:
|
||||
static std::string printElapsed (double seconds);
|
||||
|
||||
void update ();
|
||||
|
||||
typedef RippleMutex LockType;
|
||||
typedef std::lock_guard <LockType> ScopedLockType;
|
||||
LockType mLock;
|
||||
|
||||
std::uint64_t mCounts;
|
||||
int mLatencyEvents;
|
||||
std::uint64_t mLatencyMSAvg;
|
||||
std::uint64_t mLatencyMSPeak;
|
||||
std::uint64_t mTargetLatencyAvg;
|
||||
std::uint64_t mTargetLatencyPk;
|
||||
int mLastUpdate;
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user