Skip to content

Commit 1cf2ffc

Browse files
committed
Address feedback
1 parent 066f40e commit 1cf2ffc

15 files changed

+146
-46
lines changed

docs/software/commands.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,12 @@ apply.
219219
220220
* **version**: Print version info and then exit.
221221

222+
* **pregenerate-loadgen-txs**: Generate payment transactions XDR file for load testing. This command creates a file with pre-generated payment transactions that can be used with the `generateload` HTTP command (specifically the `pay_pregenerated` mode). This improves performance when running load tests with large numbers of transactions since signature verification and tx generation can be skipped.
223+
* `--count NUM-TRANSACTIONS` - number of transactions to generate (default: 1000)
224+
* `--accounts NUM-ACCOUNTS` - number of test accounts to use (default: 100)
225+
* `--offset OFFSET` - offset for account selection (default: 0)
226+
* `--output-file FILE-NAME` - file to write the generated transactions to (required)
227+
222228
## HTTP Commands
223229
Stellar-core maintains two HTTP servers, a command server and a query server.
224230
The command endpoint listens for operator commands listed below, while the query

docs/stellar-core_example.cfg

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,13 @@ LOADGEN_NUM_DATA_ENTRIES_DISTRIBUTION_FOR_TESTING=[]
683683
LOADGEN_IO_KILOBYTES_FOR_TESTING=[]
684684
LOADGEN_IO_KILOBYTES_DISTRIBUTION_FOR_TESTING=[]
685685

686+
# LOADGEN_PREGENERATED_TRANSACTIONS_FILE (string) default "stellar-load-transactions.xdr"
687+
# Path to a file containing pre-generated payment transactions for load generation.
688+
# When running load tests in PAY_PREGENERATED mode, stellar-core uses these transactions
689+
# instead of generating them on the fly, allowing for higher throughput testing.
690+
# The file can be created using the "pregenerate-loadgen-txs" command line option.
691+
LOADGEN_PREGENERATED_TRANSACTIONS_FILE="stellar-load-transactions.xdr"
692+
686693
# Transaction size in bytes for SOROBAN_INVOKE and MIX_CLASSIC_SOROBAN loadgen
687694
# modes. The probability that transactions will contain TX_SIZE_BYTES[i] bytes
688695
# is DISTRIBUTION[i] / (DISTRIBUTION[0] + DISTRIBUTION[1] + ...) for each i.
@@ -749,6 +756,12 @@ TESTING_MAX_ENTRIES_TO_ARCHIVE=100
749756
# This is only used if OVERRIDE_EVICTION_PARAMS_FOR_TESTING=true
750757
TESTING_STARTING_EVICTION_SCAN_LEVEL=6
751758

759+
# GENESIS_TEST_ACCOUNT_COUNT (integer) default 0
760+
# Number of test accounts to create in the genesis ledger when initializing a test network.
761+
# This is useful for load testing, providing a predefined set of accounts to use
762+
# in load generator.
763+
GENESIS_TEST_ACCOUNT_COUNT=0
764+
752765
#####################
753766
## Tables must come at the end. (TOML you are almost perfect!)
754767

src/herder/Herder.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,16 @@ class Herder
125125
SCPQuorumSet const& qset) = 0;
126126
virtual bool recvTxSet(Hash const& hash, TxSetXDRFrameConstPtr txset) = 0;
127127
// We are learning about a new transaction.
128+
#ifdef BUILD_TESTS
128129
// `isLoadgenTx` is true if the transaction was generated by the load
129130
// generator, and therefore can skip certain expensive validity checks
130131
virtual TransactionQueue::AddResult
131132
recvTransaction(TransactionFrameBasePtr tx, bool submittedFromSelf,
132133
bool isLoadgenTx = false) = 0;
134+
#else
135+
virtual TransactionQueue::AddResult
136+
recvTransaction(TransactionFrameBasePtr tx, bool submittedFromSelf) = 0;
137+
#endif
133138
virtual void peerDoesntHave(stellar::MessageType type,
134139
uint256 const& itemID, Peer::pointer peer) = 0;
135140
virtual TxSetXDRFrameConstPtr getTxSet(Hash const& hash) = 0;

src/herder/HerderImpl.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -590,8 +590,12 @@ HerderImpl::emitEnvelope(SCPEnvelope const& envelope)
590590
}
591591

592592
TransactionQueue::AddResult
593-
HerderImpl::recvTransaction(TransactionFrameBasePtr tx, bool submittedFromSelf,
594-
bool isLoadgenTx)
593+
HerderImpl::recvTransaction(TransactionFrameBasePtr tx, bool submittedFromSelf
594+
#ifdef BUILD_TESTS
595+
,
596+
bool isLoadgenTx
597+
#endif
598+
)
595599
{
596600
ZoneScoped;
597601
TransactionQueue::AddResult result(
@@ -618,12 +622,21 @@ HerderImpl::recvTransaction(TransactionFrameBasePtr tx, bool submittedFromSelf,
618622
}
619623
else if (!tx->isSoroban())
620624
{
621-
result = mTransactionQueue.tryAdd(tx, submittedFromSelf, isLoadgenTx);
625+
result = mTransactionQueue.tryAdd(tx, submittedFromSelf
626+
#ifdef BUILD_TESTS
627+
,
628+
isLoadgenTx
629+
#endif
630+
);
622631
}
623632
else if (mSorobanTransactionQueue)
624633
{
625-
result = mSorobanTransactionQueue->tryAdd(tx, submittedFromSelf,
626-
isLoadgenTx);
634+
result = mSorobanTransactionQueue->tryAdd(tx, submittedFromSelf
635+
#ifdef BUILD_TESTS
636+
,
637+
isLoadgenTx
638+
#endif
639+
);
627640
}
628641
else
629642
{

src/herder/HerderImpl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,15 @@ class HerderImpl : public Herder
9797
bool isLatestSlot);
9898
void emitEnvelope(SCPEnvelope const& envelope);
9999

100+
#ifdef BUILD_TESTS
100101
TransactionQueue::AddResult
101102
recvTransaction(TransactionFrameBasePtr tx, bool submittedFromSelf,
102103
bool isLoadgenTx = false) override;
104+
#else
105+
TransactionQueue::AddResult
106+
recvTransaction(TransactionFrameBasePtr tx,
107+
bool submittedFromSelf) override;
108+
#endif
103109

104110
EnvelopeStatus recvSCPEnvelope(SCPEnvelope const& envelope) override;
105111
#ifdef BUILD_TESTS

src/herder/TransactionQueue.cpp

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -328,8 +328,12 @@ validateSorobanMemo(TransactionFrameBasePtr tx)
328328
TransactionQueue::AddResult
329329
TransactionQueue::canAdd(
330330
TransactionFrameBasePtr tx, AccountStates::iterator& stateIter,
331-
std::vector<std::pair<TransactionFrameBasePtr, bool>>& txsToEvict,
332-
bool isLoadgenTx)
331+
std::vector<std::pair<TransactionFrameBasePtr, bool>>& txsToEvict
332+
#ifdef BUILD_TESTS
333+
,
334+
bool isLoadgenTx
335+
#endif
336+
)
333337
{
334338
ZoneScoped;
335339
if (isBanned(tx->getFullHash()))
@@ -465,7 +469,9 @@ TransactionQueue::canAdd(
465469
// Loadgen txs were generated by this local node, and therefore can skip
466470
// validation, and be added directly to the queue.
467471
auto txResult = tx->createSuccessResult();
472+
#ifdef BUILD_TESTS
468473
if (!isLoadgenTx)
474+
#endif
469475
{
470476
txResult =
471477
tx->checkValid(mApp.getAppConnector(), ls, 0, 0,
@@ -481,7 +487,9 @@ TransactionQueue::canAdd(
481487
// the same as getFeeSourceID()
482488
// Loadgen transactions are given unlimited funds, and therefore do no need
483489
// to be checked for fees
490+
#ifdef BUILD_TESTS
484491
if (!isLoadgenTx)
492+
#endif
485493
{
486494
auto const feeSource = ls.getAccount(tx->getFeeSourceID());
487495
auto feeStateIter = mAccountStates.find(tx->getFeeSourceID());
@@ -655,8 +663,12 @@ TransactionQueue::findAllAssetPairsInvolvedInPaymentLoops(
655663
}
656664

657665
TransactionQueue::AddResult
658-
TransactionQueue::tryAdd(TransactionFrameBasePtr tx, bool submittedFromSelf,
659-
bool isLoadgenTx)
666+
TransactionQueue::tryAdd(TransactionFrameBasePtr tx, bool submittedFromSelf
667+
#ifdef BUILD_TESTS
668+
,
669+
bool isLoadgenTx
670+
#endif
671+
)
660672
{
661673
ZoneScoped;
662674

@@ -677,7 +689,12 @@ TransactionQueue::tryAdd(TransactionFrameBasePtr tx, bool submittedFromSelf,
677689
AccountStates::iterator stateIter;
678690

679691
std::vector<std::pair<TransactionFrameBasePtr, bool>> txsToEvict;
680-
auto const res = canAdd(tx, stateIter, txsToEvict, isLoadgenTx);
692+
auto const res = canAdd(tx, stateIter, txsToEvict
693+
#ifdef BUILD_TESTS
694+
,
695+
isLoadgenTx
696+
#endif
697+
);
681698
if (res.code != TransactionQueue::AddResultCode::ADD_STATUS_PENDING)
682699
{
683700
return res;

src/herder/TransactionQueue.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,12 @@ class TransactionQueue
125125
static std::vector<AssetPair>
126126
findAllAssetPairsInvolvedInPaymentLoops(TransactionFrameBasePtr tx);
127127

128+
#ifdef BUILD_TESTS
128129
AddResult tryAdd(TransactionFrameBasePtr tx, bool submittedFromSelf,
129130
bool isLoadgenTx = false);
131+
#else
132+
AddResult tryAdd(TransactionFrameBasePtr tx, bool submittedFromSelf);
133+
#endif
130134
void removeApplied(Transactions const& txs);
131135
// Ban transactions that are no longer valid or have insufficient fee;
132136
// transaction per account limit applies here, so `txs` should have no
@@ -231,10 +235,16 @@ class TransactionQueue
231235
};
232236
BroadcastStatus broadcastTx(TimestampedTx& tx);
233237

238+
#ifdef BUILD_TESTS
234239
TransactionQueue::AddResult
235240
canAdd(TransactionFrameBasePtr tx, AccountStates::iterator& stateIter,
236241
std::vector<std::pair<TransactionFrameBasePtr, bool>>& txsToEvict,
237242
bool isLoadgenTx = false);
243+
#else
244+
TransactionQueue::AddResult
245+
canAdd(TransactionFrameBasePtr tx, AccountStates::iterator& stateIter,
246+
std::vector<std::pair<TransactionFrameBasePtr, bool>>& txsToEvict);
247+
#endif
238248

239249
void releaseFeeMaybeEraseAccountState(TransactionFrameBasePtr tx);
240250

src/main/CommandLine.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,10 +1905,10 @@ runGenerateSyntheticLoad(CommandLineArgs const& args)
19051905
VirtualClock clock;
19061906
auto app = Application::create(clock, cfg, true);
19071907
app->start();
1908-
releaseAssert(app->getLedgerManager()
1909-
.getLastClosedLedgerHeader()
1910-
.header.ledgerVersion ==
1911-
Config::CURRENT_LEDGER_PROTOCOL_VERSION);
1908+
releaseAssertOrThrow(app->getLedgerManager()
1909+
.getLastClosedLedgerHeader()
1910+
.header.ledgerVersion ==
1911+
Config::CURRENT_LEDGER_PROTOCOL_VERSION);
19121912

19131913
generateTransactions(*app, outputFile, numTransactions,
19141914
numAccounts, offset);

src/simulation/LoadGenerator.cpp

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
#include "transactions/TransactionSQL.h"
1616
#include "transactions/TransactionUtils.h"
1717
#include "transactions/test/SorobanTxTestUtils.h"
18-
#include "util/Fs.h"
1918
#include "util/Logging.h"
2019
#include "util/Math.h"
2120
#include "util/Timer.h"
@@ -298,10 +297,6 @@ LoadGenerator::stop()
298297
mLoadgenFail.Mark();
299298
reset();
300299
}
301-
302-
mPreloadedTransactionsFile.close();
303-
mFileOpened = false;
304-
mCurrPreloadedTransaction = 0;
305300
}
306301

307302
void
@@ -426,10 +421,10 @@ LoadGenerator::start(GeneratedLoadConfig& cfg)
426421

427422
if (cfg.mode == LoadGenMode::PAY_PREGENERATED)
428423
{
429-
if (!mFileOpened)
424+
if (!mPreloadedTransactionsFile)
430425
{
431-
mPreloadedTransactionsFile.open(cfg.preloadedTransactionsFile);
432-
mFileOpened = true;
426+
mPreloadedTransactionsFile = XDRInputFileStream();
427+
mPreloadedTransactionsFile->open(cfg.preloadedTransactionsFile);
433428
}
434429

435430
// Preload all accounts
@@ -518,6 +513,27 @@ LoadGenerator::scheduleLoadGeneration(GeneratedLoadConfig cfg)
518513
}
519514
}
520515

516+
if (cfg.mode == LoadGenMode::CREATE &&
517+
mApp.getConfig().GENESIS_TEST_ACCOUNT_COUNT)
518+
{
519+
errorMsg =
520+
"Cannot create accounts when GENESIS_TEST_ACCOUNT_COUNT is set";
521+
}
522+
523+
if (cfg.mode == LoadGenMode::PAY_PREGENERATED)
524+
{
525+
if (mApp.getConfig().GENESIS_TEST_ACCOUNT_COUNT == 0)
526+
{
527+
errorMsg = "PAY_PREGENERATED mode requires non-zero "
528+
"GENESIS_TEST_ACCOUNT_COUNT";
529+
}
530+
else if (cfg.preloadedTransactionsFile.empty())
531+
{
532+
errorMsg =
533+
"PAY_PREGENERATED mode requires preloadedTransactionsFile";
534+
}
535+
}
536+
521537
if (errorMsg)
522538
{
523539
CLOG_ERROR(LoadGen, "{}", *errorMsg);
@@ -832,19 +848,26 @@ LoadGenerator::generateLoad(GeneratedLoadConfig cfg)
832848
};
833849
break;
834850
case LoadGenMode::PAY_PREGENERATED:
835-
generateTx = [&]() {
836-
// Each transaction is for a unique source account, so we
837-
// should not see BAD_SEQ error codes
838-
return readTransactionFromFile(cfg);
839-
};
851+
generateTx = [&]() { return readTransactionFromFile(cfg); };
840852
break;
841853
}
842854

843-
if (submitTx(cfg, generateTx))
855+
try
844856
{
845-
--cfg.nTxs;
857+
if (submitTx(cfg, generateTx))
858+
{
859+
--cfg.nTxs;
860+
}
846861
}
847-
else if (mFailed)
862+
catch (std::runtime_error const& e)
863+
{
864+
CLOG_ERROR(LoadGen, "Exception while submitting tx: {}",
865+
e.what());
866+
mFailed = true;
867+
break;
868+
}
869+
870+
if (mFailed)
848871
{
849872
break;
850873
}
@@ -944,7 +967,11 @@ LoadGenerator::submitTx(GeneratedLoadConfig const& cfg,
944967
return false;
945968
}
946969

947-
// No re-submission in PAY_PREGENERATED mode
970+
// No re-submission in PAY_PREGENERATED mode.
971+
// Each transaction is for a unique source account, so we
972+
// should not see BAD_SEQ error codes unless core is actually dropping
973+
// txs due to overload (in which case we should just fail loadgen,
974+
// instead of re-submitting)
948975
if (++numTries >= TX_SUBMIT_MAX_TRIES ||
949976
status != TransactionQueue::AddResultCode::ADD_STATUS_ERROR ||
950977
cfg.mode == LoadGenMode::PAY_PREGENERATED)
@@ -1537,8 +1564,9 @@ LoadGenerator::execute(TransactionFrameBasePtr txf, LoadGenMode mode,
15371564
txm.mTxnBytes.Mark(xdr::xdr_argpack_size(*msg));
15381565

15391566
// Skip certain checks for pregenerated transactions
1540-
bool isLoadgenTx = (mode == LoadGenMode::PAY_PREGENERATED);
1541-
auto addResult = mApp.getHerder().recvTransaction(txf, true, isLoadgenTx);
1567+
bool isPregeneratedTx = (mode == LoadGenMode::PAY_PREGENERATED);
1568+
auto addResult =
1569+
mApp.getHerder().recvTransaction(txf, true, isPregeneratedTx);
15421570
if (addResult.code != TransactionQueue::AddResultCode::ADD_STATUS_PENDING)
15431571
{
15441572

@@ -1671,7 +1699,7 @@ GeneratedLoadConfig::txLoad(LoadGenMode mode, uint32_t nAccounts, uint32_t nTxs,
16711699
GeneratedLoadConfig
16721700
GeneratedLoadConfig::pregeneratedTxLoad(uint32_t nAccounts, uint32_t nTxs,
16731701
uint32_t txRate, uint32_t offset,
1674-
std::string const& file)
1702+
std::filesystem::path const& file)
16751703
{
16761704
GeneratedLoadConfig cfg;
16771705
cfg.mode = LoadGenMode::PAY_PREGENERATED;
@@ -1821,10 +1849,11 @@ LoadGenerator::readTransactionFromFile(GeneratedLoadConfig const& cfg)
18211849

18221850
// Read the next transaction from the file
18231851
TransactionEnvelope txEnv;
1824-
if (!mPreloadedTransactionsFile.readOne(txEnv))
1852+
releaseAssert(mPreloadedTransactionsFile);
1853+
if (!mPreloadedTransactionsFile->readOne(txEnv))
18251854
{
1826-
// End of file reached, start again from the beginning
1827-
throw std::runtime_error("LoadGenerator: End of file reached");
1855+
throw std::runtime_error("LoadGenerator: End of file reached, more "
1856+
"transactions are needed");
18281857
}
18291858

18301859
// Create a TransactionFrame from the envelope

src/simulation/LoadGenerator.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ struct GeneratedLoadConfig
9191

9292
static GeneratedLoadConfig
9393
pregeneratedTxLoad(uint32_t nAccounts, uint32_t nTxs, uint32_t txRate,
94-
uint32_t offset, std::string const& file);
94+
uint32_t offset, std::filesystem::path const& file);
9595

9696
SorobanConfig& getMutSorobanConfig();
9797
SorobanConfig const& getSorobanConfig() const;
@@ -144,7 +144,7 @@ struct GeneratedLoadConfig
144144
// Does not affect account creation.
145145
bool skipLowFeeTxs = false;
146146
// Path to the pre-generated transactions file for PAY_PREGENERATED mode
147-
std::string preloadedTransactionsFile;
147+
std::filesystem::path preloadedTransactionsFile;
148148

149149
private:
150150
SorobanConfig sorobanConfig;
@@ -269,8 +269,7 @@ class LoadGenerator
269269
std::unordered_set<uint64_t> mAccountsInUse;
270270
std::unordered_set<uint64_t> mAccountsAvailable;
271271

272-
XDRInputFileStream mPreloadedTransactionsFile;
273-
bool mFileOpened{false};
272+
std::optional<XDRInputFileStream> mPreloadedTransactionsFile;
274273
uint32_t mCurrPreloadedTransaction = 0;
275274

276275
// Get an account ID not currently in use.

0 commit comments

Comments
 (0)