diff --git a/docs/software/commands.md b/docs/software/commands.md index db17ba6a83..faec8d2ad3 100644 --- a/docs/software/commands.md +++ b/docs/software/commands.md @@ -471,7 +471,7 @@ this survey mechanism, just set `SURVEYOR_KEYS` to `$self` or a bogus key ### The following HTTP commands are exposed on test instances * **generateload** `generateload[?mode= - (create|pay|pretend|mixed_classic|soroban_upload|soroban_invoke_setup|soroban_invoke|upgrade_setup|create_upgrade|mixed_classic_soroban)&accounts=N&offset=K&txs=M&txrate=R&spikesize=S&spikeinterval=I&maxfeerate=F&skiplowfeetxs=(0|1)&dextxpercent=D&minpercentsuccess=S&instances=Y&wasms=Z&payweight=P&sorobanuploadweight=Q&sorobaninvokeweight=R]` + (create|pay|pretend|mixed_classic|soroban_upload|soroban_invoke_setup|soroban_invoke|upgrade_setup|create_upgrade|mixed_classic_soroban|stop)&accounts=N&offset=K&txs=M&txrate=R&spikesize=S&spikeinterval=I&maxfeerate=F&skiplowfeetxs=(0|1)&dextxpercent=D&minpercentsuccess=S&instances=Y&wasms=Z&payweight=P&sorobanuploadweight=Q&sorobaninvokeweight=R]` Artificially generate load for testing; must be used with `ARTIFICIALLY_GENERATE_LOAD_FOR_TESTING` set to true. @@ -532,6 +532,7 @@ this survey mechanism, just set `SURVEYOR_KEYS` to `$self` or a bogus key `soroban_upload`, and `soroban_invoke` load with the likelihood of any generated transaction falling into each mode being determined by the mode's weight divided by the sum of all weights. + * `stop` mode stops any existing load generation run and marks it as "failed". Non-`create` load generation makes use of the additional parameters: * when a nonzero `spikeinterval` is given, a spike will occur every diff --git a/src/main/CommandHandler.cpp b/src/main/CommandHandler.cpp index e2e22dbe44..29d6965e82 100644 --- a/src/main/CommandHandler.cpp +++ b/src/main/CommandHandler.cpp @@ -1263,9 +1263,18 @@ CommandHandler::generateLoad(std::string const& params, std::string& retStr) { std::map map; http::server::server::parseParams(params, map); + auto modeStr = + parseOptionalParamOrDefault(map, "mode", "create"); + // First check if a current run needs to be stopped + if (modeStr == "stop") + { + mApp.getLoadGenerator().stop(); + retStr = "Stopped load generation"; + return; + } + GeneratedLoadConfig cfg; - cfg.mode = LoadGenerator::getMode( - parseOptionalParamOrDefault(map, "mode", "create")); + cfg.mode = LoadGenerator::getMode(modeStr); cfg.nAccounts = parseOptionalParamOrDefault(map, "accounts", 1000); diff --git a/src/simulation/LoadGenerator.cpp b/src/simulation/LoadGenerator.cpp index bef7d70f91..94063d83a8 100644 --- a/src/simulation/LoadGenerator.cpp +++ b/src/simulation/LoadGenerator.cpp @@ -276,6 +276,23 @@ LoadGenerator::resetSorobanState() mContactOverheadBytes = 0; } +void +LoadGenerator::stop() +{ + ZoneScoped; + if (mStarted) + { + // Some residual transactions might still be pending in consensus, but + // that should be harmless. + if (mLoadTimer) + { + mLoadTimer->cancel(); + } + mLoadgenFail.Mark(); + reset(); + } +} + void LoadGenerator::start(GeneratedLoadConfig& cfg) { diff --git a/src/simulation/LoadGenerator.h b/src/simulation/LoadGenerator.h index 0fd898176a..2f5394dfab 100644 --- a/src/simulation/LoadGenerator.h +++ b/src/simulation/LoadGenerator.h @@ -205,6 +205,8 @@ class LoadGenerator return mContactOverheadBytes; } + void stop(); + private: struct TxMetrics { diff --git a/src/simulation/test/LoadGeneratorTests.cpp b/src/simulation/test/LoadGeneratorTests.cpp index 5cc05ee820..8025fac897 100644 --- a/src/simulation/test/LoadGeneratorTests.cpp +++ b/src/simulation/test/LoadGeneratorTests.cpp @@ -124,6 +124,26 @@ TEST_CASE("generate load with unique accounts", "[loadgen]") }, 10 * Herder::EXP_LEDGER_TIMESPAN_SECONDS, false); } + SECTION("stop loadgen") + { + loadGen.generateLoad(GeneratedLoadConfig::createAccountsLoad( + /* nAccounts */ 10000, + /* txRate */ 1)); + simulation->crankForAtLeast(std::chrono::seconds(10), false); + auto& acc = app.getMetrics().NewMeter({"loadgen", "account", "created"}, + "account"); + auto numAccounts = acc.count(); + REQUIRE(app.getMetrics() + .NewMeter({"loadgen", "run", "failed"}, "run") + .count() == 0); + loadGen.stop(); + REQUIRE(app.getMetrics() + .NewMeter({"loadgen", "run", "failed"}, "run") + .count() == 1); + // No new txs submitted + simulation->crankForAtLeast(std::chrono::seconds(10), false); + REQUIRE(acc.count() == numAccounts); + } } TEST_CASE("modify soroban network config", "[loadgen][soroban]")