Update operating mode upon network disagreement.

This commit is contained in:
Mark Travis
2019-09-06 13:03:30 -07:00
committed by Nik Bougalis
parent a9a4e2c8fb
commit e5b61c9ac9
11 changed files with 133 additions and 88 deletions

View File

@@ -895,7 +895,8 @@ RCLConsensus::Adaptor::preStartRound(RCLCxLedger const & prevLgr)
}
}
const bool synced = app_.getOPs().getOperatingMode() == NetworkOPs::omFULL;
const bool synced = app_.getOPs().getOperatingMode() ==
OperatingMode::FULL;
if (validating_)
{
@@ -950,6 +951,13 @@ RCLConsensus::Adaptor::validator() const
return !valPublic_.empty();
}
void
RCLConsensus::Adaptor::updateOperatingMode(std::size_t const positions) const
{
if (! positions && app_.getOPs().isFull())
app_.getOPs().setMode(OperatingMode::CONNECTED);
}
void
RCLConsensus::startRound(
NetClock::time_point const& now,

View File

@@ -152,6 +152,15 @@ class RCLConsensus
bool
validator() const;
/** Update operating mode based on current peer positions.
*
* If our current ledger has no agreement from the network,
* then we cannot be in the omFULL mode.
*
* @param positions Number of current peer positions.
*/
void updateOperatingMode(std::size_t const positions) const;
/** Consensus simulation parameters
*/
ConsensusParms const&

View File

@@ -2046,7 +2046,7 @@ bool ApplicationImp::serverOkay (std::string& reason)
return false;
}
if (getOPs ().getOperatingMode () < NetworkOPs::omSYNCING)
if (getOPs ().getOperatingMode () < OperatingMode::SYNCING)
{
reason = "Not synchronized with network";
return false;

View File

@@ -132,7 +132,7 @@ class NetworkOPsImp final
std::chrono::microseconds dur = std::chrono::microseconds (0);
};
OperatingMode mode_ = omDISCONNECTED;
OperatingMode mode_ = OperatingMode::DISCONNECTED;
std::array<Counters, 5> counters_;
mutable std::mutex mutex_;
std::chrono::system_clock::time_point start_ =
@@ -144,7 +144,8 @@ class NetworkOPsImp final
public:
explicit StateAccounting ()
{
counters_[omDISCONNECTED].transitions = 1;
counters_[static_cast<std::size_t>(
OperatingMode::DISCONNECTED)].transitions = 1;
}
/**
@@ -205,7 +206,8 @@ public:
, m_clock (clock)
, m_journal (journal)
, m_localTX (make_LocalTxs ())
, mMode (start_valid ? omFULL : omDISCONNECTED)
, mMode (start_valid ? OperatingMode::FULL :
OperatingMode::DISCONNECTED)
, heartbeatTimer_ (io_svc)
, clusterTimer_ (io_svc)
, mConsensus (app,
@@ -237,7 +239,14 @@ public:
{
return mMode;
}
std::string strOperatingMode (bool admin = false) const override;
std::string strOperatingMode (
OperatingMode const mode, bool const admin) const override;
std::string strOperatingMode (bool const admin = false) const override
{
return strOperatingMode(mMode, admin);
}
//
// Transaction operations.
@@ -334,7 +343,7 @@ public:
void endConsensus () override;
void setStandAlone () override
{
setMode (omFULL);
setMode (OperatingMode::FULL);
}
/** Called to initially start our timers.
@@ -356,8 +365,11 @@ public:
}
bool isFull () override
{
return !needNetworkLedger_ && (mMode == omFULL);
return !needNetworkLedger_ && (mMode == OperatingMode::FULL);
}
void setMode (OperatingMode om) override;
bool isAmendmentBlocked () override
{
return amendmentBlocked_;
@@ -518,8 +530,6 @@ private:
void processHeartbeatTimer ();
void processClusterTimer ();
void setMode (OperatingMode);
Json::Value transJson (
const STTx& stTxn, TER terResult, bool bValidated,
std::shared_ptr<ReadView const> const& lpCurrent);
@@ -612,14 +622,6 @@ static std::array<char const*, 5> const stateNames {{
"tracking",
"full"}};
#ifndef __INTELLISENSE__
static_assert (NetworkOPs::omDISCONNECTED == 0, "");
static_assert (NetworkOPs::omCONNECTED == 1, "");
static_assert (NetworkOPs::omSYNCING == 2, "");
static_assert (NetworkOPs::omTRACKING == 3, "");
static_assert (NetworkOPs::omFULL == 4, "");
#endif
std::array<char const*, 5> const NetworkOPsImp::states_ = stateNames;
std::array<Json::StaticString const, 5> const
@@ -734,9 +736,9 @@ void NetworkOPsImp::processHeartbeatTimer ()
// do we have sufficient peers? If not, we are disconnected.
if (numPeers < minPeerCount_)
{
if (mMode != omDISCONNECTED)
if (mMode != OperatingMode::DISCONNECTED)
{
setMode (omDISCONNECTED);
setMode (OperatingMode::DISCONNECTED);
JLOG(m_journal.warn())
<< "Node count (" << numPeers << ") has fallen "
<< "below required minimum (" << minPeerCount_ << ").";
@@ -750,19 +752,19 @@ void NetworkOPsImp::processHeartbeatTimer ()
return;
}
if (mMode == omDISCONNECTED)
if (mMode == OperatingMode::DISCONNECTED)
{
setMode (omCONNECTED);
setMode (OperatingMode::CONNECTED);
JLOG(m_journal.info())
<< "Node count (" << numPeers << ") is sufficient.";
}
// Check if the last validated ledger forces a change between these
// states.
if (mMode == omSYNCING)
setMode (omSYNCING);
else if (mMode == omCONNECTED)
setMode (omCONNECTED);
if (mMode == OperatingMode::SYNCING)
setMode (OperatingMode::SYNCING);
else if (mMode == OperatingMode::CONNECTED)
setMode (OperatingMode::CONNECTED);
}
mConsensus.timerEntry (app_.timeKeeper().closeTime());
@@ -818,15 +820,15 @@ void NetworkOPsImp::processClusterTimer ()
//------------------------------------------------------------------------------
std::string NetworkOPsImp::strOperatingMode (bool admin) const
std::string NetworkOPsImp::strOperatingMode (OperatingMode const mode,
bool const admin) const
{
if (mMode == omFULL && admin)
if (mode == OperatingMode::FULL && admin)
{
auto const mode = mConsensus.mode();
if (mode != ConsensusMode::wrongLedger)
auto const consensusMode = mConsensus.mode();
if (consensusMode != ConsensusMode::wrongLedger)
{
if (mode == ConsensusMode::proposing)
if (consensusMode == ConsensusMode::proposing)
return "proposing";
if (mConsensus.validating())
@@ -834,7 +836,7 @@ std::string NetworkOPsImp::strOperatingMode (bool admin) const
}
}
return states_[mMode];
return states_[static_cast<std::size_t>(mode)];
}
void NetworkOPsImp::submitTransaction (std::shared_ptr<STTx const> const& iTrans)
@@ -1146,8 +1148,8 @@ void NetworkOPsImp::apply (std::unique_lock<std::mutex>& batchLock)
e.transaction->getSTransaction());
}
if (e.applied || ((mMode != omFULL) &&
(e.failType != FailHard::yes) && e.local) ||
if (e.applied || ((mMode != OperatingMode::FULL) &&
(e.failType != FailHard::yes) && e.local) ||
(e.result == terQUEUED))
{
auto const toSkip = app_.getHashRouter().shouldRelay(
@@ -1262,7 +1264,7 @@ Json::Value NetworkOPsImp::getOwnerInfo (
void NetworkOPsImp::setAmendmentBlocked ()
{
amendmentBlocked_ = true;
setMode (omTRACKING);
setMode (OperatingMode::TRACKING);
}
bool NetworkOPsImp::checkLastClosedLedger (
@@ -1295,7 +1297,7 @@ bool NetworkOPsImp::checkLastClosedLedger (
// Will rely on peer LCL if no trusted validations exist
hash_map<uint256, std::uint32_t> peerCounts;
peerCounts[closedLedger] = 0;
if (mMode >= omTRACKING)
if (mMode >= OperatingMode::TRACKING)
peerCounts[closedLedger]++;
for (auto& peer : peerList)
@@ -1352,8 +1354,11 @@ bool NetworkOPsImp::checkLastClosedLedger (
JLOG(m_journal.info()) << "Our LCL: " << getJson(*ourClosed);
JLOG(m_journal.info()) << "Net LCL " << closedLedger;
if ((mMode == omTRACKING) || (mMode == omFULL))
setMode(omCONNECTED);
if ((mMode == OperatingMode::TRACKING)
|| (mMode == OperatingMode::FULL))
{
setMode(OperatingMode::CONNECTED);
}
if (consensus)
{
@@ -1434,10 +1439,10 @@ bool NetworkOPsImp::beginConsensus (uint256 const& networkClosed)
if(! prevLedger)
{
// this shouldn't happen unless we jump ledgers
if (mMode == omFULL)
if (mMode == OperatingMode::FULL)
{
JLOG(m_journal.warn()) << "Don't have LCL, going to tracking";
setMode (omTRACKING);
setMode (OperatingMode::TRACKING);
}
return false;
@@ -1524,31 +1529,33 @@ void NetworkOPsImp::endConsensus ()
if (networkClosed.isZero ())
return;
// WRITEME: Unless we are in omFULL and in the process of doing a consensus,
// WRITEME: Unless we are in FULL and in the process of doing a consensus,
// we must count how many nodes share our LCL, how many nodes disagree with
// our LCL, and how many validations our LCL has. We also want to check
// timing to make sure there shouldn't be a newer LCL. We need this
// information to do the next three tests.
if (((mMode == omCONNECTED) || (mMode == omSYNCING)) && !ledgerChange)
if (((mMode == OperatingMode::CONNECTED)
|| (mMode == OperatingMode::SYNCING)) && !ledgerChange)
{
// Count number of peers that agree with us and UNL nodes whose
// validations we have for LCL. If the ledger is good enough, go to
// omTRACKING - TODO
// TRACKING - TODO
if (!needNetworkLedger_)
setMode (omTRACKING);
setMode (OperatingMode::TRACKING);
}
if (((mMode == omCONNECTED) || (mMode == omTRACKING)) && !ledgerChange)
if (((mMode == OperatingMode::CONNECTED)
|| (mMode == OperatingMode::TRACKING)) && !ledgerChange)
{
// check if the ledger is good enough to go to omFULL
// Note: Do not go to omFULL if we don't have the previous ledger
// check if the ledger is bad enough to go to omCONNECTED -- TODO
// check if the ledger is good enough to go to FULL
// Note: Do not go to FULL if we don't have the previous ledger
// check if the ledger is bad enough to go to CONNECTE D -- TODO
auto current = m_ledgerMaster.getCurrentLedger();
if (app_.timeKeeper().now() <
(current->info().parentCloseTime + 2* current->info().closeTimeResolution))
{
setMode (omFULL);
setMode (OperatingMode::FULL);
}
}
@@ -1557,8 +1564,11 @@ void NetworkOPsImp::endConsensus ()
void NetworkOPsImp::consensusViewChange ()
{
if ((mMode == omFULL) || (mMode == omTRACKING))
setMode (omCONNECTED);
if ((mMode == OperatingMode::FULL)
|| (mMode == OperatingMode::TRACKING))
{
setMode (OperatingMode::CONNECTED);
}
}
void NetworkOPsImp::pubManifest (Manifest const& mo)
@@ -1794,19 +1804,19 @@ void NetworkOPsImp::pubPeerStatus (
void NetworkOPsImp::setMode (OperatingMode om)
{
using namespace std::chrono_literals;
if (om == omCONNECTED)
if (om == OperatingMode::CONNECTED)
{
if (app_.getLedgerMaster ().getValidatedLedgerAge () < 1min)
om = omSYNCING;
om = OperatingMode::SYNCING;
}
else if (om == omSYNCING)
else if (om == OperatingMode::SYNCING)
{
if (app_.getLedgerMaster ().getValidatedLedgerAge () >= 1min)
om = omCONNECTED;
om = OperatingMode::CONNECTED;
}
if ((om > omTRACKING) && amendmentBlocked_)
om = omTRACKING;
if ((om > OperatingMode::TRACKING) && amendmentBlocked_)
om = OperatingMode::TRACKING;
if (mMode == om)
return;
@@ -2455,7 +2465,7 @@ void NetworkOPsImp::pubLedger (
jvObj[jss::txn_count] = Json::UInt (alpAccepted->getTxnCount ());
if (mMode >= omSYNCING)
if (mMode >= OperatingMode::SYNCING)
{
jvObj[jss::validated_ledgers]
= app_.getLedgerMaster ().getCompleteLedgers ();
@@ -2826,7 +2836,7 @@ bool NetworkOPsImp::subLedger (InfoSub::ref isrListener, Json::Value& jvResult)
jvResult[jss::reserve_inc] = Json::UInt (lpClosed->fees().increment);
}
if ((mMode >= omSYNCING) && !isNeedNetworkLedger ())
if ((mMode >= OperatingMode::SYNCING) && !isNeedNetworkLedger ())
{
jvResult[jss::validated_ledgers]
= app_.getLedgerMaster ().getCompleteLedgers ();
@@ -3358,9 +3368,9 @@ void NetworkOPsImp::StateAccounting::mode (OperatingMode om)
auto now = std::chrono::system_clock::now();
std::lock_guard lock (mutex_);
++counters_[om].transitions;
counters_[mode_].dur += std::chrono::duration_cast<
std::chrono::microseconds>(now - start_);
++counters_[static_cast<std::size_t>(om)].transitions;
counters_[static_cast<std::size_t>(mode_)].dur +=
std::chrono::duration_cast<std::chrono::microseconds>(now - start_);
mode_ = om;
start_ = now;
@@ -3379,12 +3389,13 @@ NetworkOPsImp::StateAccounting::json() const
auto const current = std::chrono::duration_cast<
std::chrono::microseconds>(std::chrono::system_clock::now() - start);
counters[mode].dur += current;
counters[static_cast<std::size_t>(mode)].dur += current;
Json::Value ret = Json::objectValue;
for (std::underlying_type_t<OperatingMode> i = omDISCONNECTED;
i <= omFULL; ++i)
for (std::size_t i = static_cast<std::size_t>(
OperatingMode::DISCONNECTED);
i <= static_cast<std::size_t>(OperatingMode::FULL); ++i)
{
ret[states_[i]] = Json::objectValue;
auto& state = ret[states_[i]];

View File

@@ -55,6 +55,25 @@ class ValidatorKeys;
// code assumes this node is synched (and will continue to do so until
// there's a functional network.
//
/** Specifies the mode under which the server believes it's operating.
This has implications about how the server processes transactions and
how it responds to requests (e.g. account balance request).
@note Other code relies on the numerical values of these constants; do
not change them without verifying each use and ensuring that it is
not a breaking change.
*/
enum class OperatingMode
{
DISCONNECTED = 0, //!< not ready to process requests
CONNECTED = 1, //!< convinced we are talking to the network
SYNCING = 2, //!< fallen slightly behind
TRACKING = 3, //!< convinced we agree with the network
FULL = 4 //!< we have the ledger and can even validate
};
/** Provides server functionality for clients.
Clients include backend applications, local commands, and connected
@@ -76,16 +95,6 @@ protected:
public:
using clock_type = beast::abstract_clock <std::chrono::steady_clock>;
enum OperatingMode
{
// how we process transactions or account balance requests
omDISCONNECTED = 0, // not ready to process requests
omCONNECTED = 1, // convinced we are talking to the network
omSYNCING = 2, // fallen slightly behind
omTRACKING = 3, // convinced we agree with the network
omFULL = 4 // we have the ledger and can even validate
};
enum class FailHard : unsigned char
{
no,
@@ -105,7 +114,9 @@ public:
//
virtual OperatingMode getOperatingMode () const = 0;
virtual std::string strOperatingMode (bool admin = false) const = 0;
virtual std::string strOperatingMode (
OperatingMode const mode, bool const admin = false) const = 0;
virtual std::string strOperatingMode (bool const admin = false) const = 0;
//--------------------------------------------------------------------------
//
@@ -171,6 +182,7 @@ public:
virtual void clearNeedNetworkLedger () = 0;
virtual bool isNeedNetworkLedger () = 0;
virtual bool isFull () = 0;
virtual void setMode(OperatingMode om) = 0;
virtual bool isAmendmentBlocked () = 0;
virtual void setAmendmentBlocked () = 0;
virtual void consensusViewChange () = 0;

View File

@@ -650,17 +650,17 @@ SHAMapStoreImp::health()
if (stop_)
return Health::stopping;
}
if (!netOPs_)
if (! netOPs_)
return Health::ok;
NetworkOPs::OperatingMode mode = netOPs_->getOperatingMode();
constexpr static std::chrono::seconds age_threshold(60);
auto age = ledgerMaster_->getValidatedLedgerAge();
if (mode != NetworkOPs::omFULL || age.count() >= ageThreshold_)
OperatingMode mode = netOPs_->getOperatingMode();
if (mode != OperatingMode::FULL || age > age_threshold)
{
JLOG(journal_.warn()) << "Not deleting. state: " << mode
<< " age " << age.count()
<< " age threshold " << ageThreshold_;
JLOG(journal_.warn()) << "Not deleting. state: "
<< app_.getOPs().strOperatingMode(mode, false)
<< ". age " << age.count() << 's';
healthy_ = false;
}

View File

@@ -1285,6 +1285,7 @@ Consensus<Adaptor>::phaseEstablish()
JLOG(j_.info()) << "Converge cutoff (" << currPeerPositions_.size()
<< " participants)";
adaptor_.updateOperatingMode(currPeerPositions_.size());
prevProposers_ = currPeerPositions_.size();
prevRoundTime_ = result_->roundTime.read();
phase_ = ConsensusPhase::accepted;

View File

@@ -150,7 +150,7 @@ error_code_i fillHandler (Context& context,
return rpcNO_PERMISSION;
if ((handler->condition_ & NEEDS_NETWORK_CONNECTION) &&
(context.netOps.getOperatingMode () < NetworkOPs::omSYNCING))
(context.netOps.getOperatingMode () < OperatingMode::SYNCING))
{
JLOG (context.j.info())
<< "Insufficient network mode for RPC: "

View File

@@ -212,11 +212,12 @@ ShardArchiveHandler::complete(path dstPath)
{
// If validating and not synced then defer and retry
auto const mode {ptr->app_.getOPs().getOperatingMode()};
if (ptr->validate_ && mode != NetworkOPs::omFULL)
if (ptr->validate_ && mode != OperatingMode::FULL)
{
std::lock_guard lock(m_);
timer_.expires_from_now(static_cast<std::chrono::seconds>(
(NetworkOPs::omFULL - mode) * 10));
(static_cast<std::size_t>(OperatingMode::FULL)
- static_cast<std::size_t>(mode)) * 10));
timer_.async_wait(
[=, dstPath = std::move(dstPath), ptr = std::move(ptr)]
(boost::system::error_code const& ec)

View File

@@ -40,7 +40,6 @@ class SHAMapStore_test : public beast::unit_test::suite
cfg->LEDGER_HISTORY = deleteInterval;
auto& section = cfg->section(ConfigSection::nodeDatabase());
section.set("online_delete", to_string(deleteInterval));
//section.set("age_threshold", "60");
return cfg;
}

View File

@@ -865,6 +865,10 @@ struct Peer
return runAsValidator;
}
void
updateOperatingMode(std::size_t const positions) const
{}
//--------------------------------------------------------------------------
// A locally submitted transaction
void