Skip to content

Commit d3043a4

Browse files
authored
Merge branch 'master' into call-hz-tests
2 parents b3a7d19 + 350acec commit d3043a4

23 files changed

Lines changed: 668 additions & 145 deletions

Cargo.lock

Lines changed: 16 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/herder/RustQuorumCheckerAdaptor.cpp

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,17 @@ fromQuorumCheckerStatusJson(Json::Value const& value)
159159
}
160160

161161
auto statusInt = value.asUInt();
162-
if (statusInt > static_cast<unsigned>(QuorumCheckerStatus::UNKNOWN))
163-
{
162+
switch (statusInt)
163+
{
164+
case static_cast<unsigned>(QuorumCheckerStatus::UNSAT):
165+
case static_cast<unsigned>(QuorumCheckerStatus::SAT):
166+
case static_cast<unsigned>(QuorumCheckerStatus::UNKNOWN):
167+
case static_cast<unsigned>(QuorumCheckerStatus::NO_QUORUM):
168+
return static_cast<QuorumCheckerStatus>(statusInt);
169+
default:
164170
throw RustQuorumCheckerError("Invalid status value: " +
165171
std::to_string(statusInt));
166172
}
167-
168-
return static_cast<QuorumCheckerStatus>(statusInt);
169173
}
170174

171175
void
@@ -252,6 +256,7 @@ QuorumCheckerMetrics::QuorumCheckerMetrics()
252256
, mAbortedRun(0)
253257
, mResultPotentialSplit(0)
254258
, mResultUnknown(0)
259+
, mResultNoQuorum(0)
255260
, mCumulativeTimeMs(0)
256261
, mCumulativeMemByte(0)
257262
{
@@ -293,6 +298,12 @@ QuorumCheckerMetrics::QuorumCheckerMetrics(Json::Value const& value)
293298
throw RustQuorumCheckerError(
294299
"Metrics missing or invalid 'result_unknown_count' field");
295300
}
301+
if (!value.isMember("result_no_quorum_count") ||
302+
!value["result_no_quorum_count"].isUInt())
303+
{
304+
throw RustQuorumCheckerError(
305+
"Metrics missing or invalid 'result_no_quorum_count' field");
306+
}
296307
if (!value.isMember("cumulative_time_ms") ||
297308
!value["cumulative_time_ms"].isUInt64())
298309
{
@@ -310,6 +321,7 @@ QuorumCheckerMetrics::QuorumCheckerMetrics(Json::Value const& value)
310321
mAbortedRun = value["aborted_run_count"].asUInt64();
311322
mResultPotentialSplit = value["result_potential_split_count"].asUInt64();
312323
mResultUnknown = value["result_unknown_count"].asUInt64();
324+
mResultNoQuorum = value["result_no_quorum_count"].asUInt64();
313325
mCumulativeTimeMs = value["cumulative_time_ms"].asUInt64();
314326
mCumulativeMemByte = value["cumulative_mem_byte"].asUInt64();
315327
}
@@ -323,6 +335,7 @@ QuorumCheckerMetrics::toJson()
323335
ret["aborted_run_count"] = Json::UInt64(mAbortedRun);
324336
ret["result_potential_split_count"] = Json::UInt64(mResultPotentialSplit);
325337
ret["result_unknown_count"] = Json::UInt64(mResultUnknown);
338+
ret["result_no_quorum_count"] = Json::UInt64(mResultNoQuorum);
326339
ret["cumulative_time_ms"] = Json::UInt64(mCumulativeTimeMs);
327340
ret["cumulative_mem_byte"] = Json::UInt64(mCumulativeMemByte);
328341
return ret;
@@ -337,6 +350,7 @@ QuorumCheckerMetrics::flush(MetricsRegistry& metrics)
337350
metrics.NewCounter({"scp", "qic", "result-potential-split"})
338351
.inc(mResultPotentialSplit);
339352
metrics.NewCounter({"scp", "qic", "result-unknown"}).inc(mResultUnknown);
353+
metrics.NewCounter({"scp", "qic", "result-no-quorum"}).inc(mResultNoQuorum);
340354
metrics.NewMeter({"scp", "qic", "cumulative-time-ms"}, "milli-second")
341355
.Mark(mCumulativeTimeMs);
342356
metrics.NewMeter({"scp", "qic", "cumulative-mem-byte"}, "byte")
@@ -346,6 +360,7 @@ QuorumCheckerMetrics::flush(MetricsRegistry& metrics)
346360
mAbortedRun = 0;
347361
mResultPotentialSplit = 0;
348362
mResultUnknown = 0;
363+
mResultNoQuorum = 0;
349364
mCumulativeTimeMs = 0;
350365
mCumulativeMemByte = 0;
351366
}
@@ -396,6 +411,10 @@ checkQuorumIntersectionInner(
396411
{
397412
metrics.mResultPotentialSplit += 1;
398413
}
414+
else if (status == QuorumCheckerStatus::NO_QUORUM)
415+
{
416+
metrics.mResultNoQuorum += 1;
417+
}
399418

400419
// Update time limit. Memory is a transient resource that gets reclaimed
401420
// back.
@@ -565,10 +584,12 @@ runQuorumIntersectionCheckAsync(
565584

566585
// Note: the ecode should match the return code from the command
567586
// line-running process which is just `QuorumCheckerStatus` as integer
568-
// on success. However, if the command fails due to abort (if exceeding
569-
// the memory limit), the ecode=1 will be returned because of the
570-
// simplification of collapsing all non-WIFEXITED exits to error code 1
571-
// (see `mapExitStatusToErrorCode` in ProcessManagerImpl.cpp).
587+
// on success. Exceeding the time or (estimated) memory limit is now a
588+
// recoverable solver error that surfaces as `UNKNOWN` (102), not a
589+
// process abort. If the command dies abnormally (crash/signal, i.e. a
590+
// non-WIFEXITED exit), ecode=1 is returned because of the
591+
// simplification of collapsing all such exits to error code 1 (see
592+
// `mapExitStatusToErrorCode` in ProcessManagerImpl.cpp).
572593
int ecode = ec.value();
573594
CLOG_DEBUG(SCP,
574595
"Processing quorum intersection check result: numNodes={}, "
@@ -579,17 +600,23 @@ runQuorumIntersectionCheckAsync(
579600

580601
if (ecode == static_cast<int>(QuorumCheckerStatus::UNSAT) ||
581602
ecode == static_cast<int>(QuorumCheckerStatus::SAT) ||
582-
ecode == static_cast<int>(QuorumCheckerStatus::UNKNOWN))
603+
ecode == static_cast<int>(QuorumCheckerStatus::UNKNOWN) ||
604+
ecode == static_cast<int>(QuorumCheckerStatus::NO_QUORUM))
583605
{
584606
try
585607
{
586608
auto res = parseResultsJson(qicResultJson);
587609
hStateSP->mStatus = fromQuorumCheckerStatusJson(res["status"]);
588610
QuorumCheckerMetrics metrics(res["metrics"]);
589611
metrics.flush(hStateSP->mMetrics);
590-
// only update the following info if we had a complete run
612+
// only update the following info if we had a complete run.
613+
// NO_QUORUM is a complete result (the checker definitively
614+
// determined the FBAS has no quorum) so we record it too, but
615+
// unlike UNSAT it is not a "good" result and must not advance
616+
// mLastGoodLedger.
591617
if (ecode == static_cast<int>(QuorumCheckerStatus::UNSAT) ||
592-
ecode == static_cast<int>(QuorumCheckerStatus::SAT))
618+
ecode == static_cast<int>(QuorumCheckerStatus::SAT) ||
619+
ecode == static_cast<int>(QuorumCheckerStatus::NO_QUORUM))
593620
{
594621
hStateSP->mNumNodes = numNodes;
595622
hStateSP->mLastCheckLedger = ledger;

src/herder/RustQuorumCheckerAdaptor.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct QuorumCheckerMetrics
3535
uint64_t mAbortedRun;
3636
uint64_t mResultPotentialSplit;
3737
uint64_t mResultUnknown;
38+
uint64_t mResultNoQuorum;
3839
uint64_t mCumulativeTimeMs;
3940
uint64_t mCumulativeMemByte;
4041
QuorumCheckerMetrics();
@@ -52,16 +53,20 @@ struct QuorumCheckerMetrics
5253
// - The time limit applies across all runs (all loops during criticality
5354
// analysis), and is enforced by the Rust implementation, which returns an
5455
// `Err` if exceeded.
55-
// - The memory limit is enforced by the glocal allocator, and once exceeds,
56-
// will abort the program immediately.
56+
// - The memory limit is enforced softly inside the solver: memory usage is
57+
// conservatively estimated from the solver's clause/variable counts, and the
58+
// solver returns an `Err` once the estimate exceeds the limit.
5759
//
58-
// Therefore it is **crucial** this routine runs in a separate process from the
59-
// main stellar-core!!
60+
// We still run this routine in a separate process from the main stellar-core so
61+
// that its (potentially large) resource usage stays isolated.
6062
//
6163
// Return values:
6264
// - `UNSAT` if the quorum intersection check finds no non-intersecting quorums
6365
// (good)
6466
// - `SAT` if the quorum intersection check finds quorum splits (bad!!)
67+
// - `NO_QUORUM` if the FBAS contains no quorum at all (a degenerate /
68+
// potential-halt configuration). This is distinct from `UNSAT`: a network
69+
// with no quorum does not enjoy quorum intersection.
6570
// - `UNKNOWN` if the quorum intersection check does not complete, likely due to
6671
// exceeding solver internal limits (e.g. no. conflicts). Note: if the quorum
6772
// intersection check completes, but the criticality analysis

0 commit comments

Comments
 (0)