Skip to content

Commit

Permalink
Merge pull request #2553 from hidenori-shinohara/simulate-command-line
Browse files Browse the repository at this point in the history
Implement a spike feature for generateload

Reviewed-by: MonsieurNicolas
  • Loading branch information
latobarita authored Jun 11, 2020
2 parents 9f46f7e + f1247de commit c5c8c74
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 28 deletions.
3 changes: 2 additions & 1 deletion docs/software/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,12 +264,13 @@ format.

### The following HTTP commands are exposed on test instances
* **generateload**
`generateload[?mode=(create|pay)&accounts=N&offset=K&txs=M&txrate=R&batchsize=L]`<br>
`generateload[?mode=(create|pay)&accounts=N&offset=K&txs=M&txrate=R&batchsize=L&spikesize=S&spikeinterval=I]`<br>
Artificially generate load for testing; must be used with
`ARTIFICIALLY_GENERATE_LOAD_FOR_TESTING` set to true. Depending on the mode,
either creates new accounts or generates payments on accounts specified
(where number of accounts can be offset). Additionally, allows batching up to
100 account creations per transaction via 'batchsize'.
When a nonzero I is given, a spike will occur every I seconds injecting S transactions on top of `txrate`.

* **manualclose**
If MANUAL_CLOSE is set to true in the .cfg file. This will cause the current
Expand Down
4 changes: 3 additions & 1 deletion src/main/Application.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,9 @@ class Application
// against the current application.
virtual void generateLoad(bool isCreate, uint32_t nAccounts,
uint32_t offset, uint32_t nTxs, uint32_t txRate,
uint32_t batchSize) = 0;
uint32_t batchSize,
std::chrono::seconds spikeInterval,
uint32_t spikeSize) = 0;

// Access the load generator for manual operation.
virtual LoadGenerator& getLoadGenerator() = 0;
Expand Down
6 changes: 4 additions & 2 deletions src/main/ApplicationImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -569,11 +569,13 @@ ApplicationImpl::manualClose()
void
ApplicationImpl::generateLoad(bool isCreate, uint32_t nAccounts,
uint32_t offset, uint32_t nTxs, uint32_t txRate,
uint32_t batchSize)
uint32_t batchSize,
std::chrono::seconds spikeInterval,
uint32_t spikeSize)
{
getMetrics().NewMeter({"loadgen", "run", "start"}, "run").Mark();
getLoadGenerator().generateLoad(isCreate, nAccounts, offset, nTxs, txRate,
batchSize);
batchSize, spikeInterval, spikeSize);
}

LoadGenerator&
Expand Down
4 changes: 3 additions & 1 deletion src/main/ApplicationImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ class ApplicationImpl : public Application
#ifdef BUILD_TESTS
virtual void generateLoad(bool isCreate, uint32_t nAccounts,
uint32_t offset, uint32_t nTxs, uint32_t txRate,
uint32_t batchSize) override;
uint32_t batchSize,
std::chrono::seconds spikeInterval,
uint32_t spikeSize) override;

virtual LoadGenerator& getLoadGenerator() override;
#endif
Expand Down
16 changes: 11 additions & 5 deletions src/main/CommandHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,8 @@ CommandHandler::generateLoad(std::string const& params, std::string& retStr)
uint32_t txRate = 10;
uint32_t batchSize = 100; // Only for account creations
uint32_t offset = 0;
uint32_t spikeSize = 0;
uint32_t spikeIntervalInt = 0;
std::string mode = "create";

std::map<std::string, std::string> map;
Expand All @@ -801,20 +803,24 @@ CommandHandler::generateLoad(std::string const& params, std::string& retStr)
maybeParseParam(map, "batchsize", batchSize);
maybeParseParam(map, "offset", offset);
maybeParseParam(map, "txrate", txRate);
maybeParseParam(map, "spikeinterval", spikeIntervalInt);
std::chrono::seconds spikeInterval(spikeIntervalInt);
maybeParseParam(map, "spikesize", spikeSize);

uint32_t numItems = isCreate ? nAccounts : nTxs;
std::string itemType = isCreate ? "accounts" : "txs";
double hours = (numItems / txRate) / 3600.0;

if (batchSize > 100)
{
batchSize = 100;
retStr = "Setting batch size to its limit of 100.";
}
mApp.generateLoad(isCreate, nAccounts, offset, nTxs, txRate, batchSize);
retStr +=
fmt::format(" Generating load: {:d} {:s}, {:d} tx/s = {:f} hours",
numItems, itemType, txRate, hours);

mApp.generateLoad(isCreate, nAccounts, offset, nTxs, txRate, batchSize,
spikeInterval, spikeSize);

retStr += fmt::format(" Generating load: {:d} {:s}, {:d} tx/s",
numItems, itemType, txRate);
}
else
{
Expand Down
11 changes: 6 additions & 5 deletions src/simulation/CoreTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ TEST_CASE(
auto& app = *nodes[0]; // pick a node to generate load

auto& lg = app.getLoadGenerator();
lg.generateLoad(true, 3, 0, 0, 10, 100);
lg.generateLoad(true, 3, 0, 0, 10, 100, std::chrono::seconds(0), 0);
try
{
simulation->crankUntil(
Expand All @@ -374,7 +374,7 @@ TEST_CASE(
},
3 * Herder::EXP_LEDGER_TIMESPAN_SECONDS, false);

lg.generateLoad(false, 3, 0, 10, 10, 100);
lg.generateLoad(false, 3, 0, 10, 10, 100, std::chrono::seconds(0), 0);
simulation->crankUntil(
[&]() {
return simulation->haveAllExternalized(8, 2) &&
Expand Down Expand Up @@ -486,7 +486,7 @@ TEST_CASE("Accounts vs latency", "[scalability][!hide]")
uint32_t numItems = 500000;

// Create accounts
lg.generateLoad(true, numItems, 0, 0, 10, 100);
lg.generateLoad(true, numItems, 0, 0, 10, 100, std::chrono::seconds(0), 0);

auto& complete =
appPtr->getMetrics().NewMeter({"loadgen", "run", "complete"}, "run");
Expand All @@ -501,7 +501,8 @@ TEST_CASE("Accounts vs latency", "[scalability][!hide]")
txtime.Clear();

// Generate payment txs
lg.generateLoad(false, numItems, 0, numItems / 10, 10, 100);
lg.generateLoad(false, numItems, 0, numItems / 10, 10, 100,
std::chrono::seconds(0), 0);
while (!io.stopped() && complete.count() == 1)
{
clock.crank();
Expand Down Expand Up @@ -535,7 +536,7 @@ netTopologyTest(std::string const& name,
auto& app = *nodes[0];

auto& lg = app.getLoadGenerator();
lg.generateLoad(true, 50, 0, 0, 10, 100);
lg.generateLoad(true, 50, 0, 0, 10, 100, std::chrono::seconds(0), 0);
auto& complete =
app.getMetrics().NewMeter({"loadgen", "run", "complete"}, "run");

Expand Down
37 changes: 27 additions & 10 deletions src/simulation/LoadGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ LoadGenerator::createRootAccount()
}

int64_t
LoadGenerator::getTxPerStep(uint32_t txRate)
LoadGenerator::getTxPerStep(uint32_t txRate, std::chrono::seconds spikeInterval,
uint32_t spikeSize)
{
if (!mStartTime)
{
Expand All @@ -91,6 +92,14 @@ LoadGenerator::getTxPerStep(uint32_t txRate)
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - *mStartTime);
auto txs = bigDivide(elapsed.count(), txRate, 1000, Rounding::ROUND_DOWN);
if (spikeInterval.count() > 0)
{
txs +=
bigDivide(std::chrono::duration_cast<std::chrono::seconds>(elapsed)
.count(),
1, spikeInterval.count(), Rounding::ROUND_DOWN) *
spikeSize;
}

if (txs <= mTotalSubmitted)
{
Expand All @@ -115,7 +124,9 @@ LoadGenerator::reset()
void
LoadGenerator::scheduleLoadGeneration(bool isCreate, uint32_t nAccounts,
uint32_t offset, uint32_t nTxs,
uint32_t txRate, uint32_t batchSize)
uint32_t txRate, uint32_t batchSize,
std::chrono::seconds spikeInterval,
uint32_t spikeSize)
{
// If previously scheduled step of load did not succeed, fail this loadgen
// run.
Expand All @@ -138,9 +149,10 @@ LoadGenerator::scheduleLoadGeneration(bool isCreate, uint32_t nAccounts,
{
mLoadTimer->expires_from_now(std::chrono::milliseconds(STEP_MSECS));
mLoadTimer->async_wait(
[this, nAccounts, offset, nTxs, txRate, batchSize, isCreate]() {
[this, nAccounts, offset, nTxs, txRate, batchSize, isCreate,
spikeInterval, spikeSize]() {
this->generateLoad(isCreate, nAccounts, offset, nTxs, txRate,
batchSize);
batchSize, spikeInterval, spikeSize);
},
&VirtualTimer::onFailureNoop);
}
Expand All @@ -151,9 +163,11 @@ LoadGenerator::scheduleLoadGeneration(bool isCreate, uint32_t nAccounts,
<< mApp.getState();
mLoadTimer->expires_from_now(std::chrono::seconds(10));
mLoadTimer->async_wait(
[this, nAccounts, offset, nTxs, txRate, batchSize, isCreate]() {
[this, nAccounts, offset, nTxs, txRate, batchSize, isCreate,
spikeInterval, spikeSize]() {
this->scheduleLoadGeneration(isCreate, nAccounts, offset, nTxs,
txRate, batchSize);
txRate, batchSize, spikeInterval,
spikeSize);
},
&VirtualTimer::onFailureNoop);
}
Expand All @@ -165,7 +179,10 @@ LoadGenerator::scheduleLoadGeneration(bool isCreate, uint32_t nAccounts,
// with the remainder.
void
LoadGenerator::generateLoad(bool isCreate, uint32_t nAccounts, uint32_t offset,
uint32_t nTxs, uint32_t txRate, uint32_t batchSize)
uint32_t nTxs, uint32_t txRate, uint32_t batchSize,
std::chrono::seconds spikeInterval,
uint32_t spikeSize)

{
if (!mStartTime)
{
Expand Down Expand Up @@ -193,7 +210,7 @@ LoadGenerator::generateLoad(bool isCreate, uint32_t nAccounts, uint32_t offset,
batchSize = 1;
}

auto txPerStep = getTxPerStep(txRate);
auto txPerStep = getTxPerStep(txRate, spikeInterval, spikeSize);
auto& submitTimer =
mApp.getMetrics().NewTimer({"loadgen", "step", "submit"});
auto submitScope = submitTimer.TimeScope();
Expand Down Expand Up @@ -232,8 +249,8 @@ LoadGenerator::generateLoad(bool isCreate, uint32_t nAccounts, uint32_t offset,

mLastSecond = now;
mTotalSubmitted += txPerStep;
scheduleLoadGeneration(isCreate, nAccounts, offset, nTxs, txRate,
batchSize);
scheduleLoadGeneration(isCreate, nAccounts, offset, nTxs, txRate, batchSize,
spikeInterval, spikeSize);
}

uint32_t
Expand Down
15 changes: 12 additions & 3 deletions src/simulation/LoadGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,14 @@ class LoadGenerator
// given target number of accounts and txs, and a given target tx/s rate.
// If work remains after the current step, call scheduleLoadGeneration()
// with the remainder.
// txRate: The number of transactions per second when there is no spike.
// spikeInterval: A spike will occur every spikeInterval seconds.
// Set this to 0 if no spikes are needed.
// spikeSize: The number of transactions a spike injects on top of the
// steady rate.
void generateLoad(bool isCreate, uint32_t nAccounts, uint32_t offset,
uint32_t nTxs, uint32_t txRate, uint32_t batchSize);
uint32_t nTxs, uint32_t txRate, uint32_t batchSize,
std::chrono::seconds spikeInterval, uint32_t spikeSize);

// Verify cached accounts are properly reflected in the database
// return any accounts that are inconsistent.
Expand Down Expand Up @@ -99,12 +105,15 @@ class LoadGenerator

void reset();
void createRootAccount();
int64_t getTxPerStep(uint32_t txRate);
int64_t getTxPerStep(uint32_t txRate, std::chrono::seconds spikeInterval,
uint32_t spikeSize);

// Schedule a callback to generateLoad() STEP_MSECS miliseconds from now.
void scheduleLoadGeneration(bool isCreate, uint32_t nAccounts,
uint32_t offset, uint32_t nTxs, uint32_t txRate,
uint32_t batchSize);
uint32_t batchSize,
std::chrono::seconds spikeInterval,
uint32_t spikeSize);

std::vector<Operation> createAccounts(uint64_t i, uint64_t batchSize,
uint32_t ledgerNum);
Expand Down

0 comments on commit c5c8c74

Please sign in to comment.