17#include <CLI11/CLI11.hpp>
20#include <nlohmann/json.hpp>
31 int threads = std::thread::hardware_concurrency();
32 if (core_id > threads || core_id < 0)
42 CPU_SET(core_id, &set);
44 if (sched_setaffinity(0,
sizeof(cpu_set_t), &set) < 0)
82 const std::string& default_label,
83 const std::string& default_pid_file,
86 pid_file(fmt::format(
"{}.pid", default_pid_file))
89 app.set_config(
"--config");
96 "Identifier for this client, written to {}",
perf_summary))
97 ->capture_default_str();
103 "Path to which the client PID will be written")
104 ->capture_default_str();
107 cli::add_address_option(
111 "Remote node JSON RPC address to which requests should be sent")
116 ->check(CLI::ExistingFile);
119 ->check(CLI::ExistingFile);
120 app.add_option(
"--ca",
ca_file)->required(
true)->check(CLI::ExistingFile);
121 app.add_option(
"--bearer-token",
bearer_token)->required(
false);
127 "Verify results against expectation, specified in file")
129 ->check(CLI::ExistingFile);
133 "--transaction-rate",
135 "The number of transactions per second to send");
142 "The basic number of transactions to send (will actually send this "
143 "many for each thread, in each session)")
144 ->capture_default_str();
145 app.add_option(
"-t,--threads",
thread_count)->capture_default_str();
146 app.add_option(
"-s,--sessions",
session_count)->capture_default_str();
149 "--max-writes-ahead",
151 "How many transactions the client should send without waiting for "
152 "responses. 0 will send all transactions before blocking for any "
153 "responses, 1 will minimise latency by serially waiting for each "
154 "transaction's response, other values may provide a balance between "
155 "throughput and latency")
156 ->capture_default_str();
158 app.add_option(
"--latency-rounds",
latency_rounds)->capture_default_str();
161 app.add_flag(
"--sign",
sign,
"Send client-signed transactions")
162 ->capture_default_str();
164 .add_flag(
"--no-create",
no_create,
"Skip creation/setup transactions")
165 ->capture_default_str();
170 "Don't wait for transactions to be globally committed")
171 ->capture_default_str();
176 "Write tx sent and received times to csv")
177 ->capture_default_str();
182 "Use non-deterministically random transaction contents each run")
183 ->capture_default_str();
188 "Check every JSON response for errors. Potentially slow")
189 ->capture_default_str();
196 template <
typename TOptions>
209 std::string key_id =
"Invalid";
210 std::shared_ptr<::tls::Cert> tls_cert =
nullptr;
220 throw std::logic_error(
"Response failed check");
226 (reply.
status == HTTP_STATUS_OK ||
227 reply.
status == HTTP_STATUS_NO_CONTENT))
229 const auto tx_id = timing::extract_transaction_id(reply);
231 if (!tx_id.has_value())
233 throw std::logic_error(
"No transaction ID found in response headers");
241 throw std::logic_error(fmt::format(
242 "View went backwards (expected {}, saw {})!",
250 throw std::logic_error(fmt::format(
251 "There has been an election and transactions have "
252 "been lost! (saw {}.{}, currently at {}.{})",
263 void append_prepared_tx(
264 const PreparedTx& tx,
const std::optional<size_t>& index)
266 if (index.has_value())
298 const bool is_first_time = tls_cert ==
nullptr;
312 tls_cert = std::make_shared<::tls::Cert>(
313 std::make_shared<::tls::CA>(ca), cert_pem, key);
316 const auto [
host, port] = ccf::split_net_address(
options.server_address);
318 std::make_shared<RpcTlsClient>(
host, port,
nullptr, tls_cert, key_id);
320 if (
options.sign && !force_unsigned)
323 conn->create_key_pair(key);
326 conn->set_prefix(
"app");
332 "Connected to server via TLS ({})", conn->get_ciphersuite_name());
339 const std::string& method,
340 const std::span<const uint8_t> params,
342 const std::optional<size_t>& index)
348 ccf::http::headervalues::contenttype::JSON,
350 options.bearer_token.size() == 0 ?
nullptr :
355 append_prepared_tx(tx, index);
360 return std::accumulate(
361 txs.begin(), txs.end(), 0, [](
size_t n,
const PreparedTx& tx) {
362 return n + tx.rpc.encoded.size();
378 return r.
status == HTTP_STATUS_OK;
388 std::shared_ptr<RpcTlsClient>& connection,
const PreparedTxs& txs)
393 if (
options.transactions_per_s > 0)
396 std::chrono::nanoseconds{1000000000 /
options.transactions_per_s};
397 connection->set_tcp_nodelay(
true);
404 for (
size_t session = 1; session <=
options.session_count; ++session)
410 while (written < txs.size())
411 write(txs[written], read, written, connection);
416 if (session !=
options.session_count)
429 auto timing_results =
end_timing(last_commit);
431 return timing_results;
445 const std::shared_ptr<RpcTlsClient>& connection)
465 while (read < written)
467 const auto r = connection->read_response_non_blocking();
474 process_reply(r.value());
479 if (
options.max_writes_ahead > 0)
482 while (written - read >=
options.max_writes_ahead)
484 process_reply(connection->read_response());
493 const std::shared_ptr<RpcTlsClient>& connection)
496 while (read < written)
498 process_reply(connection->read_response());
503 void reconnect(std::shared_ptr<RpcTlsClient>& connection)
509 const std::shared_ptr<RpcTlsClient>& connection,
514 p[
"transaction_id"] = fmt::format(
"{}.{}", view, seqno);
515 return connection->get(
"tx", p);
524 const auto it = expected.find(
"seed");
525 if (it != expected.end())
527 const auto expected_seed =
528 it->get<
decltype(
options.generator_seed)>();
529 if (expected_seed !=
options.generator_seed)
531 throw std::runtime_error(fmt::format(
532 "Verification file expects seed {}, but currently using {}",
540 const auto it = expected.find(
"transactions");
541 if (it != expected.end())
543 const auto expected_txs =
544 it->get<
decltype(
options.num_transactions)>();
545 if (expected_txs !=
options.num_transactions)
547 throw std::runtime_error(fmt::format(
548 "Verification file is only applicable for {} transactions, but "
549 "currently running {}",
557 const auto it = expected.find(
"sessions");
558 if (it != expected.end())
560 const auto expected_sessions =
561 it->get<
decltype(
options.session_count)>();
562 if (expected_sessions !=
options.session_count)
564 throw std::runtime_error(fmt::format(
565 "Verification file is only applicable for {} sessions, but "
566 "currently running {}",
574 bool expected_randomise =
false;
575 const auto it = expected.find(
"randomise");
576 if (it != expected.end())
578 expected_randomise = it->get<
bool>();
581 if (expected_randomise !=
options.randomise)
583 throw std::runtime_error(fmt::format(
584 "Verification file is only applicable when randomisation is {}, "
585 "but this option is currently {}",
586 expected_randomise ?
"ON" :
"OFF",
587 options.randomise ?
"ON" :
"OFF"));
627 last_response.has_value() &&
635 catch (std::exception& e)
637 LOG_FAIL_FMT(
"Exception during creation steps: {}", e.what());
650 catch (std::exception& e)
665 catch (std::exception& e)
681 const auto tx_id = timing::extract_transaction_id(response);
682 if (!tx_id.has_value())
684 throw std::logic_error(
685 "Cannot wait for response to commit - it does not have a TxID");
695 throw std::logic_error(
696 "timing is already set - has begin_timing been called multiple "
707 throw std::logic_error(
708 "timing is not set - has begin_timing not been called?");
715 options.no_wait, end_highest_local_commit,
options.latency_rounds);
717 catch (
const std::runtime_error& e)
736 using namespace chrono;
741 duration_cast<milliseconds>(timing_results.
duration).count();
742 const auto duration = dur_ms / 1000.0;
743 const auto tx_per_sec = total_txs / duration;
746 "{} transactions took {}ms.\n"
755 " All txs (local_commit): {}\n"
756 " Global commit: {}\n",
762 for (
size_t round = 0; round < timing_results.
per_round.size(); ++round)
764 const auto& round_info = timing_results.
per_round[round];
767 " Round {} (req ids #{} to #{})\n"
771 round_info.begin_rpc_id,
772 round_info.end_rpc_id,
773 round_info.local_commit,
774 round_info.global_commit);
778 std::ofstream perf_summary_csv(
780 if (perf_summary_csv.is_open())
784 const auto total_bytes =
787 const auto [
host, _] = ccf::split_net_address(
options.server_address);
789 perf_summary_csv << duration_cast<milliseconds>(
792 perf_summary_csv <<
"," << dur_ms;
793 perf_summary_csv <<
","
794 << (
host.find(
"127.") == 0 ?
796 options.label + string(
"_distributed"));
797 perf_summary_csv <<
"," << total_bytes;
798 perf_summary_csv <<
"," <<
options.thread_count;
799 perf_summary_csv <<
"," << (double)dur_ms / total_txs;
800 perf_summary_csv <<
"," << total_txs;
803 perf_summary_csv <<
"," << lc.
average;
804 perf_summary_csv <<
"," << lc.sample_count;
807 perf_summary_csv <<
"," << gc.
average;
808 perf_summary_csv <<
"," << gc.sample_count;
810 perf_summary_csv << endl;
821 options.generator_seed = std::random_device()();
825 "Random choices determined by seed: {}",
options.generator_seed);
836 const bool verifying = !
options.verification_file.empty();
859 "Sending {} transactions from {} clients {} times...",
878 template <
typename T>
881 std::uniform_int_distribution<T> dist;
885 template <
typename T>
888 std::uniform_int_distribution<T> dist(0, exclusive_upper_bound - 1);
892 template <
typename T>
893 T
rand_range(T inclusive_lower_bound, T exclusive_upper_bound)
895 std::uniform_int_distribution<T> dist(
896 inclusive_lower_bound, exclusive_upper_bound - 1);
Definition sha256_hash.h:16
std::string hex_str() const
Definition sha256_hash.cpp:61
Definition perf_client.h:198
void prepare_all_transactions()
Definition perf_client.h:643
timing::Results end_timing(size_t end_highest_local_commit)
Definition perf_client.h:703
virtual void prepare_transactions()=0
virtual void post_timing_body_hook()
Definition perf_client.h:385
virtual void post_creation_hook()
Definition perf_client.h:382
void write(const PreparedTx &tx, size_t &read, size_t &written, const std::shared_ptr< RpcTlsClient > &connection)
Definition perf_client.h:441
timing::ResponseTimes response_times
Definition perf_client.h:289
void kick_off_timing()
Definition perf_client.h:434
static size_t total_byte_size(const PreparedTxs &txs)
Definition perf_client.h:358
TOptions options
Definition perf_client.h:278
std::shared_ptr< RpcTlsClient > get_connection()
Definition perf_client.h:612
void add_prepared_ser_tx(const std::string &method, const std::span< const uint8_t > params, bool expects_commit, const std::optional< size_t > &index)
Definition perf_client.h:338
void wait_for_global_commit(const RpcTlsClient::Response &response)
Definition perf_client.h:677
virtual std::optional< RpcTlsClient::Response > send_creation_transactions()
Definition perf_client.h:370
virtual void run()
Definition perf_client.h:814
T rand_range(T exclusive_upper_bound)
Definition perf_client.h:886
timing::Results send_all_prepared_transactions()
Definition perf_client.h:657
virtual void pre_timing_body_hook()
Definition perf_client.h:384
std::vector< PreparedTx > PreparedTxs
Definition perf_client.h:284
void wait_for_global_commit(const ccf::TxID &target)
Definition perf_client.h:672
std::chrono::nanoseconds write_delay_ns
Definition perf_client.h:293
std::shared_ptr< RpcTlsClient > create_connection(bool force_unsigned=false)
Definition perf_client.h:295
virtual timing::Results call_raw_batch(std::shared_ptr< RpcTlsClient > &connection, const PreparedTxs &txs)
Definition perf_client.h:387
ccf::TxID last_response_tx_id
Definition perf_client.h:290
void summarize_results(const timing::Results &timing_results)
Definition perf_client.h:733
PreparedTxs prepared_txs
Definition perf_client.h:287
T rand_range()
Definition perf_client.h:879
std::shared_ptr< RpcTlsClient > rpc_connection
Definition perf_client.h:286
void begin_timing()
Definition perf_client.h:691
void reconnect(std::shared_ptr< RpcTlsClient > &connection)
Definition perf_client.h:503
virtual void verify_params(const nlohmann::json &expected)
Definition perf_client.h:518
virtual bool check_response(const RpcTlsClient::Response &r)
Definition perf_client.h:375
RpcTlsClient::Response get_tx_status(const std::shared_ptr< RpcTlsClient > &connection, size_t view, size_t seqno)
Definition perf_client.h:508
virtual void verify_final_state(const nlohmann::json &expected)
Definition perf_client.h:592
void blocking_read(size_t &read, size_t written, const std::shared_ptr< RpcTlsClient > &connection)
Definition perf_client.h:490
PerfBase(const TOptions &o)
Definition perf_client.h:595
std::chrono::high_resolution_clock::time_point last_write_time
Definition perf_client.h:292
virtual void pre_creation_hook()
Definition perf_client.h:381
T rand_range(T inclusive_lower_bound, T exclusive_upper_bound)
Definition perf_client.h:893
std::mt19937 rand_generator
Definition perf_client.h:280
virtual void verify_initial_state(const nlohmann::json &expected)
Definition perf_client.h:591
void send_all_creation_transactions()
Definition perf_client.h:618
void init_connection()
Definition perf_client.h:603
nlohmann::json verification_target
Definition perf_client.h:282
void stop_timing()
Definition timing.h:195
void write_to_file(const string &filename)
Definition timing.h:540
void start_timing()
Definition timing.h:184
void record_receive(size_t rpc_id, const optional< ccf::TxID > &tx_id, size_t global_seqno=0)
Definition timing.h:212
bool is_timing_active()
Definition timing.h:190
Results produce_results(bool allow_pending, size_t highest_local_commit, size_t desired_rounds=1)
Definition timing.h:308
void record_send(const std::string &method, size_t rpc_id, bool expects_commit)
Definition timing.h:205
void wait_for_global_commit(const ccf::TxID &target, bool record=true)
Definition timing.h:223
#define LOG_INFO_FMT
Definition logger.h:395
#define LOG_TRACE_FMT
Definition logger.h:378
#define LOG_DEBUG_FMT
Definition logger.h:380
#define LOG_FATAL_FMT
Definition logger.h:397
#define LOG_FAIL_FMT
Definition logger.h:396
std::vector< uint8_t > cert_pem_to_der(const Pem &pem)
Definition verifier.cpp:38
ccf::NodeInfoNetwork::NetAddress ParsedAddress
Definition cli_helper.h:18
Definition perf_client.h:26
bool pin_to_core(int core_id)
Definition perf_client.h:29
HttpRpcTlsClient RpcTlsClient
Definition rpc_tls_client.h:248
constexpr auto perf_summary
Definition perf_client.h:27
std::vector< uint8_t > slurp(const std::string &file, bool optional=false)
Tries to read a file as byte vector.
Definition files.h:43
void dump(const std::vector< uint8_t > &data, const std::string &file)
Writes the content of a vector to a file.
Definition files.h:121
std::string slurp_string(const std::string &file, bool optional=false)
Tries to read a file as string.
Definition files.h:82
nlohmann::json slurp_json(const std::string &file, bool optional=false)
Tries to read a file as JSON.
Definition files.h:106
Definition configuration.h:13
bool status_success(http_status status)
Definition http_parser.h:71
SeqNo seqno
Definition tx_id.h:46
View view
Definition tx_id.h:45
Definition rpc_tls_client.h:23
size_t id
Definition rpc_tls_client.h:25
std::vector< uint8_t > encoded
Definition rpc_tls_client.h:24
Definition rpc_tls_client.h:29
size_t id
Definition rpc_tls_client.h:30
http_status status
Definition rpc_tls_client.h:31
Definition perf_client.h:201
std::string method
Definition perf_client.h:203
bool expects_commit
Definition perf_client.h:204
RpcTlsClient::PreparedRpc rpc
Definition perf_client.h:202
Definition perf_client.h:55
bool sign
Definition perf_client.h:72
size_t latency_rounds
Definition perf_client.h:68
size_t generator_seed
Definition perf_client.h:69
std::string label
Definition perf_client.h:58
cli::ParsedAddress server_address
Definition perf_client.h:61
std::string pid_file
Definition perf_client.h:59
PerfOptions(const std::string &default_label, const std::string &default_pid_file, CLI::App &app)
Definition perf_client.h:81
size_t max_writes_ahead
Definition perf_client.h:67
std::string key_file
Definition perf_client.h:62
bool relax_commit_target
Definition perf_client.h:78
size_t thread_count
Definition perf_client.h:65
size_t num_transactions
Definition perf_client.h:64
bool check_responses
Definition perf_client.h:77
size_t transactions_per_s
Definition perf_client.h:70
bool no_create
Definition perf_client.h:73
size_t session_count
Definition perf_client.h:66
std::string cert_file
Definition perf_client.h:62
std::string ca_file
Definition perf_client.h:62
bool write_tx_times
Definition perf_client.h:75
std::string verification_file
Definition perf_client.h:62
std::string bearer_token
Definition perf_client.h:62
bool randomise
Definition perf_client.h:76
bool no_wait
Definition perf_client.h:74
double average
Definition timing.h:24
size_t total_sends
Definition timing.h:133
Measure total_global_commit
Definition timing.h:139
Clock::time_point start_time
Definition timing.h:135
Measure total_local_commit
Definition timing.h:138
TimeDelta duration
Definition timing.h:136
size_t total_receives
Definition timing.h:134
vector< PerRound > per_round
Definition timing.h:150