Skip to content

Add new mode to apply load for tx set validation - #5404

Open
SirTyson wants to merge 1 commit into
stellar:masterfrom
SirTyson:tx-set-validation-apply-load
Open

Add new mode to apply load for tx set validation#5404
SirTyson wants to merge 1 commit into
stellar:masterfrom
SirTyson:tx-set-validation-apply-load

Conversation

@SirTyson

@SirTyson SirTyson commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Description

This adds a new mode to apply load, allowing us for more "end to end" simulations. In the new mode, we time and simulate ballot phase through the end of apply, but in a single node network such that SCP state transitions occur instantly. This is useful, as it allows us to benchmark the processing overhead associated with consensus, as well as measure cache performance and dedup similar work done during tx set validation and the later application of this transaction set.

The motivation for this test was from my recent block latency experiments. I noticed how optimizations in network calls, reducing bandwidth, etc were not moving the needle on actual block latency. It turned out that consensus latency was greatly affected by CPU based bottlenecks, not just network calls. On supercluster, it's challenging to benchmark CPU costs of concensus, as you can't easily run tracy or profilers and have limited visibility to single node performance. This test allows us to much more easily measure and improve the processing heavy aspects of concensus. In overlay-v2-shared for a block with 6K SAC transfers, we saw about 300 ms of non-network "ingestion" time when receiving a tx set and 500 ms of apply. Imo this was a significant blind spot in our single node apply load tests previously.

Note that the actual number produced by this test is meaningless, as consensus without a network tells us very little about overall performance. However, the reported phase timings are very useful in identifying bottlenecks and comparing solutions. This simulates the processing done by a non-leader node. In the future, it may also be helpful to add a mode for block construction as well.

Checklist

  • Reviewed the contributing document
  • Rebased on top of master (no merge commits)
  • Ran clang-format v8.0.0 (via make format or the Visual Studio extension)
  • Compiles
  • Ran all tests
  • If change impacts performance, include supporting evidence per the performance document

@SirTyson
SirTyson requested review from dmkozh and marta-lokhova and a balanced review from Copilot August 4, 2026 20:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an optional apply-load benchmark path covering tx-set decoding, validation, local consensus, and ledger application.

Changes:

  • Adds configurable validation-and-apply timing.
  • Records phase timings and signature-cache metrics.
  • Documents the mode and new validation metric.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/simulation/ApplyLoad.h Declares timing paths and phase statistics.
src/simulation/ApplyLoad.cpp Implements consensus-backed benchmarking.
src/main/Config.h Defines timing configuration.
src/main/Config.cpp Parses timing configuration.
src/herder/HerderSCPDriver.h Declares validation timer.
src/herder/HerderSCPDriver.cpp Instruments tx-set validation.
docs/software/commands.md Documents apply-load timing paths.
docs/metrics.md Documents the validation metric.
docs/apply-load-benchmark-sac.cfg Adds example timing configuration.

Comment on lines +1110 to +1112
if (measuresTxSetValidation())
{
closeLedgerViaConsensus(txs, recordUtilization);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, we can just add a GENERATE for the modes in the existing acceptance tests.

Comment thread src/herder/HerderSCPDriver.cpp
Comment thread docs/software/commands.md
Comment on lines +23 to +26
synthetic transactions. The benchmark omits the overlay and mempool, but
each iteration reconstructs the tx set from serialized bytes and runs real
consensus with the node as its own single-validator quorum. It does not
simulate network transport, peer fetching, or multi-node timing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just say that it may measure txset stuff

APPLY_LOAD_MODE="benchmark"
APPLY_LOAD_MODEL_TX="sac"

# Which timing path to use: "apply" preserves the historical apply-only

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I don't think it's correct to call the apply-only mode 'historical', both have a valid use case

Comment thread docs/software/commands.md
Comment on lines +23 to +26
synthetic transactions. The benchmark omits the overlay and mempool, but
each iteration reconstructs the tx set from serialized bytes and runs real
consensus with the node as its own single-validator quorum. It does not
simulate network transport, peer fetching, or multi-node timing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just say that it may measure txset stuff

if (pRes == nullptr)
{
std::string zoneTxt("miss");
ZoneText(zoneTxt.c_str(), zoneTxt.size());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: You should be able to use one-line ZoneNamed here, as your string is static (same for 'hit' branch)


private:
bool
measuresTxSetValidation() const

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Could you please move this implementation into cpp file as well?

# Which timing path to use: "apply" preserves the historical apply-only
# benchmark, while "txset-validation-and-apply" simulates a non-leader receiving
# and validating a tx set before applying it. Tx-set creation is not measured.
APPLY_LOAD_TIMING_PHASES = "apply"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to add this to all the benchmark configs

}

void
ApplyLoad::logPhaseStats(std::string const& label,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be a member function

mPhaseEndToEndMs.end(), 0.0);
if (e2eSum > 0.0)
{
CLOG_WARNING(Perf, "txset validation share of end-to-end: {:.2f}%",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it make more sense to compute statistics on ratios, instead of just outputting effectively the ratio of means? I think understanding the variance in share is useful, and we're hiding it here.

if (mMode == ApplyLoadMode::MAX_SAC_TPS)
{

if (measuresTxSetValidation())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the gates on measuresTxSetValidation are kind of brittle as they're missing the actual intention of the check and sometimes do not exhaust all the options explicitly when they should.

I think we could future-proof the code a bit and add more targeted checks: e.g. here we're specifically interested in mode not being apply, so we should update the check and message to mode != APPLY. Another example: closeBenchmarkLedger should probably use an exhaustive switch instead of an if. logConfiguredPhaseStats actually doesn't log anything for the apply-only mode, even though from the name one would assume that it does. Basically we should gate the code semantically instead of a blanket check on one of the enum variants.

I realize this is kind of minor for now, but it's very easy for apply load to grow unwieldy (and it's already pretty complex), so I'd like to start with more robust code in the first place.

Comment on lines +1110 to +1112
if (measuresTxSetValidation())
{
closeLedgerViaConsensus(txs, recordUtilization);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, we can just add a GENERATE for the modes in the existing acceptance tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants