Skip to content

Commit

Permalink
Implement multiple patterns in udp-server-h2client
Browse files Browse the repository at this point in the history
Allow to provide multiple patterns to udp-server-h2client,
which will be selected by rand() algorithm [0].

Also, histogram buckets are now provided as a comma-separated
list, to avoid problems parsing bash arguments as space-separated [1].

Finally, we upgrade to http2comm v2.1.3 to have dynamic counter for
response status codes [2].

[0] #100
[1] #101
[2] testillano/http2comm#22
  • Loading branch information
testillano authored and Eduardo Ramos Testillano (eramedu) committed Aug 5, 2023
1 parent 5912b77 commit 7cbdda8
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 61 deletions.
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ Also, `st/repeat.sh` script repeats a previous execution (last by default) in he
So you may start the process, again, natively or using docker:

```bash
$> OPTS=(--verbose --traffic-server-worker-threads 5 --prometheus-response-delay-seconds-histogram-boundaries "100e-6 200e-6 300e-6 400e-6 500e-6 1e-3 5e-3 10e-3 20e-3")
$> OPTS=(--verbose --traffic-server-worker-threads 5 --prometheus-response-delay-seconds-histogram-boundaries "100e-6,200e-6,300e-6,400e-6,1e-3,5e-3,10e-3,20e-3")
$> build/Release/bin/h2agent "${OPTS[@]}" # native executable
- or -
$> docker run --rm -it --network=host -v $(pwd -P):$(pwd -P) ghcr.io/testillano/h2agent:latest "${OPTS[@]}" # docker
Expand Down Expand Up @@ -792,14 +792,13 @@ Options:
[--prometheus-port <port>]
Prometheus local <port>; defaults to 8080.
[--prometheus-response-delay-seconds-histogram-boundaries <space-separated list of doubles>]
[--prometheus-response-delay-seconds-histogram-boundaries <comma-separated list of doubles>]
Bucket boundaries for response delay seconds histogram; no boundaries are defined by default.
Scientific notation is allowed, so in terms of microseconds (e-6) and milliseconds (e-3) we
could provide, for example: "100e-6 200e-6 300e-6 400e-6 500e-6 1e-3 5e-3 10e-3 20e-3".
Scientific notation is allowed, i.e.: "100e-6,200e-6,300e-6,400e-6,1e-3,5e-3,10e-3,20e-3".
This affects to both mock server-data and client-data processing time values,
but normally both flows will not be used together in the same process instance.
[--prometheus-message-size-bytes-histogram-boundaries <space-separated list of doubles>]
[--prometheus-message-size-bytes-histogram-boundaries <comma-separated list of doubles>]
Bucket boundaries for Rx/Tx message size bytes histogram; no boundaries are defined by default.
This affects to both mock 'server internal/client external' message size values,
but normally both flows will not be used together in the same process instance.
Expand Down Expand Up @@ -1131,12 +1130,11 @@ To print accumulated statistics you can send UDP message 'STATS' or stop/interru
[--prometheus-port <port>]
Prometheus local <port>; defaults to 8081. Value of -1 disables metrics.
[--prometheus-response-delay-seconds-histogram-boundaries <space-separated list of doubles>]
[--prometheus-response-delay-seconds-histogram-boundaries <comma-separated list of doubles>]
Bucket boundaries for response delay seconds histogram; no boundaries are defined by default.
Scientific notation is allowed, so in terms of microseconds (e-6) and milliseconds (e-3) we
could provide, for example: "100e-6 200e-6 300e-6 400e-6 500e-6 1e-3 5e-3 10e-3 20e-3".
Scientific notation is allowed, i.e.: "100e-6,200e-6,300e-6,400e-6,1e-3,5e-3,10e-3,20e-3".
[--prometheus-message-size-bytes-histogram-boundaries <space-separated list of doubles>]
[--prometheus-message-size-bytes-histogram-boundaries <comma-separated list of doubles>]
Bucket boundaries for Tx/Rx message size bytes histogram; no boundaries are defined by default.
[-h|--help]
Expand Down Expand Up @@ -1199,9 +1197,8 @@ status codes: 3 2xx, 0 3xx, 0 4xx, 0 5xx, 0 timeouts, 0 connection errors
## Execution of udp-client utility
This utility could be useful to test `udp-server`, and specially, `udp-server-h2client` tool.
You can also use netcat in bash, to generate messages easily, but this tool provide high load.
This tool manages a monotonically increasing sequence within a given range, and allow to parse
it over a pattern to build the datagram generated.
You can also use netcat in bash, to generate messages easily, but this tool provide high load. This tool manages a monotonically increasing sequence within a given range, and allow to parse it over a pattern to build the datagram generated. Even, we could provide a list of patterns which will be randomized.
Although we could launch multiple UDP clients towards the UDP server (such server must be unique due to non-oriented connection nature of UDP protocol), it is probably unnecessary: this client is fast enough to generate the required load.
### Command line
Expand All @@ -1227,8 +1224,11 @@ Options:
[-f|--final <value>]
Final value for datagram. Defaults to unlimited.
[-p|--pattern <value>]
[--pattern <value>]
Pattern to be parsed by sequence (@{seq} is replaced by sequence). Defaults to '@{seq}'.
This parameter can occur multiple times to create a random set. For example, passing
'--pattern foo --pattern foo --pattern bar', there is a probability of 2/3 to select
'foo' and 1/3 to select 'bar'.
[-e|--print-each <value>]
Print messages each specific amount (must be positive). Defaults to 1.
Expand Down
2 changes: 1 addition & 1 deletion build-native.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ ert_queuedispatcher_ver=v1.0.3
jupp0r_prometheuscpp_ver=v0.13.0
civetweb_civetweb_ver=v1.14
ert_metrics_ver=v1.0.3
ert_http2comm_ver=v2.1.2
ert_http2comm_ver=v2.1.3
nlohmann_json_ver=$(grep ^nlohmann_json_ver__dflt= ${REPO_DIR}/build.sh | cut -d= -f2)
pboettch_jsonschemavalidator_ver=$(grep ^pboettch_jsonschemavalidator_ver__dflt= ${REPO_DIR}/build.sh | cut -d= -f2)
google_test_ver=$(grep ^google_test_ver__dflt= ${REPO_DIR}/build.sh | cut -d= -f2)
Expand Down
42 changes: 28 additions & 14 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,27 +95,40 @@ const char* AdminApiVersion = "v1";
// Auxiliary functions //
/////////////////////////

// Transform input in the form "<double> <double> .. <double>" to bucket boundaries vector
// Cientific notation is allowed, for example boundaries for 150us would be 150e-6
// Transform input in the form "<double>,<double>,..,<double>" to bucket boundaries vector
// Cientific notation is allowed, for example boundary for 150us would be 150e-6
// Returns the final string ignoring non-double values scanned. Also sort is applied.
std::string loadHistogramBoundaries(const std::string &input, ert::metrics::bucket_boundaries_t &boundaries) {
std::string result;

std::stringstream ss(input);
double value = 0;
while (ss >> value) {
boundaries.push_back(value);
std::istringstream ss(input);
std::string item;

while (std::getline(ss, item, ',')) {
try {
double value = std::stod(item);
if (value >= 0) {
boundaries.push_back(value);
result += (std::to_string(value) + ",");
}
else {
std::cerr << "Ignoring negative double: " << item << "\n";
}
} catch (const std::invalid_argument& e) {
std::cerr << "Ignoring invalid double: " << item << "\n";
} catch (const std::out_of_range& e) {
std::cerr << "Ignoring out-of-range double: " << item << "\n";
}
}

// Sort surviving numbers:
std::sort(boundaries.begin(), boundaries.end());
for (const auto &i: boundaries) {
result += (std::to_string(i) + " ");
}

result.pop_back(); // remove last comma

return result;
}


/*
int getThreadCount() {
char buf[512];
Expand Down Expand Up @@ -359,14 +372,13 @@ void usage(int rc, const std::string &errorMessage = "")
<< "[--prometheus-port <port>]\n"
<< " Prometheus local <port>; defaults to 8080.\n\n"

<< "[--prometheus-response-delay-seconds-histogram-boundaries <space-separated list of doubles>]\n"
<< "[--prometheus-response-delay-seconds-histogram-boundaries <comma-separated list of doubles>]\n"
<< " Bucket boundaries for response delay seconds histogram; no boundaries are defined by default.\n"
<< " Scientific notation is allowed, so in terms of microseconds (e-6) and milliseconds (e-3) we\n"
<< " could provide, for example: \"100e-6 200e-6 300e-6 400e-6 500e-6 1e-3 5e-3 10e-3 20e-3\".\n"
<< " Scientific notation is allowed, i.e.: \"100e-6,200e-6,300e-6,400e-6,1e-3,5e-3,10e-3,20e-3\".\n"
<< " This affects to both mock server-data and client-data processing time values,\n"
<< " but normally both flows will not be used together in the same process instance.\n\n"

<< "[--prometheus-message-size-bytes-histogram-boundaries <space-separated list of doubles>]\n"
<< "[--prometheus-message-size-bytes-histogram-boundaries <comma-separated list of doubles>]\n"
<< " Bucket boundaries for Rx/Tx message size bytes histogram; no boundaries are defined by default.\n"
<< " This affects to both mock 'server internal/client external' message size values,\n"
<< " but normally both flows will not be used together in the same process instance.\n\n"
Expand Down Expand Up @@ -717,6 +729,8 @@ int main(int argc, char* argv[])
bool hasPEMpasswordPrompt = (admin_secured && traffic_secured && traffic_server_key_password.empty());

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
std::cout << '\n';

std::cout << currentDateTime() << ": Starting " << progname << " " << (gitVersion.empty() ? "":gitVersion) << '\n';
std::cout << "Log level: " << ert::tracing::Logger::levelAsString(ert::tracing::Logger::getLevel()) << '\n';
std::cout << "Verbose (stdout): " << (verbose ? "true":"false") << '\n';
Expand Down
4 changes: 1 addition & 3 deletions src/model/Transformation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -623,9 +623,7 @@ std::string Transformation::asString() const {
else if (source_type_ == SourceType::RandomSet || source_type_ == SourceType::ServerEvent) {
ss << " | source_tokenized_:";
for(auto it: source_tokenized_) {
ss<< " '" ;
ss << it;
ss << "'";
ss << " '" << it << "'";
}
}
else if (source_type_ == SourceType::STxtFile || source_type_ == SourceType::SBinFile) {
Expand Down
58 changes: 42 additions & 16 deletions tools/udp-client/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,11 @@ void usage(int rc, const std::string &errorMessage = "")
<< "[-f|--final <value>]\n"
<< " Final value for datagram. Defaults to unlimited.\n\n"

<< "[-p|--pattern <value>]\n"
<< " Pattern to be parsed by sequence (@{seq} is replaced by sequence). Defaults to '@{seq}'.\n\n"
<< "[--pattern <value>]\n"
<< " Pattern to be parsed by sequence (@{seq} is replaced by sequence). Defaults to '@{seq}'.\n"
<< " This parameter can occur multiple times to create a random set. For example, passing\n"
<< " '--pattern foo --pattern foo --pattern bar', there is a probability of 2/3 to select\n"
<< " 'foo' and 1/3 to select 'bar'.\n\n"

<< "[-e|--print-each <value>]\n"
<< " Print messages each specific amount (must be positive). Defaults to 1.\n\n"
Expand Down Expand Up @@ -144,18 +147,22 @@ double toDouble(const std::string& value)
return result;
}

bool cmdOptionExists(char** begin, char** end, const std::string& option,
std::string& value)
char **cmdOptionExists(char** begin, char** end, const std::string& option, std::string& value)
{
char** itr = std::find(begin, end, option);
bool exists = (itr != end);
char** result = std::find(begin, end, option);
bool exists = (result != end);

if (exists && ++itr != end)
{
value = *itr;
if (exists) {
if (++result != end)
{
value = *result;
}
}
else {
result = nullptr;
}

return exists;
return result;
}

void sighndl(int signal)
Expand All @@ -173,14 +180,15 @@ void sighndl(int signal)

int main(int argc, char* argv[])
{
srand(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
progname = basename(argv[0]);

// Parse command-line ///////////////////////////////////////////////////////////////////////////////////////
int i_printEach = 1; // default
std::string udpSocketPath{};
unsigned long long int initialValue{};
unsigned long long int finalValue = std::numeric_limits<unsigned long long>::max();
std::string pattern = "@{seq}";
std::vector<std::string> patterns{};
double eps = 1.0;

std::string value;
Expand Down Expand Up @@ -215,10 +223,10 @@ int main(int argc, char* argv[])
finalValue = toLong(value);
}

if (cmdOptionExists(argv, argv + argc, "-p", value)
|| cmdOptionExists(argv, argv + argc, "--pattern", value))
char **next = argv;
while ((next = cmdOptionExists(next, argv + argc, "--pattern", value)))
{
pattern = value;
patterns.push_back(value);
}

if (cmdOptionExists(argv, argv + argc, "-e", value)
Expand All @@ -232,11 +240,21 @@ int main(int argc, char* argv[])
std::cout << '\n';

if (udpSocketPath.empty()) usage(EXIT_FAILURE);
if (patterns.empty()) patterns.push_back("@{seq}"); // default if no --pattern parameter is provided

std::cout << "Path: " << udpSocketPath << '\n';
std::cout << "Print each: " << i_printEach << " message(s)\n";
std::cout << "Range: [" << initialValue << ", " << finalValue << "]\n";
std::cout << "Pattern: " << pattern << "\n";
if (patterns.size() == 1) {
std::cout << "Pattern: " << patterns[0] << "\n";
}
else {
std::cout << "Patterns (random subset): ";
for(auto it: patterns) {
std::cout << " '" << it << "'";
}
std::cout << '\n';
}
std::cout << "Events per second: ";
if (eps > 0) std::cout << eps;
else std::cout << "unlimited";
Expand Down Expand Up @@ -276,11 +294,19 @@ int main(int argc, char* argv[])

unsigned long long int sequence{};
auto startTimeNS = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch());
int patternsSize = patterns.size();

while (true) {

udpDataSeq = std::to_string(initialValue + sequence);
udpData = pattern;

if (patternsSize == 1) {
udpData = udpData = patterns[0];
}
else { // random subset
udpData = patterns[rand () % patternsSize];
}

// search/replace @{seq} by 'udpDataSeq':
pos = 0u;
while((pos = udpData.find(from, pos)) != std::string::npos) {
Expand Down
41 changes: 27 additions & 14 deletions tools/udp-server-h2client/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,11 @@ void usage(int rc, const std::string &errorMessage = "")
<< "[--prometheus-port <port>]\n"
<< " Prometheus local <port>; defaults to 8081. Value of -1 disables metrics.\n\n"

<< "[--prometheus-response-delay-seconds-histogram-boundaries <space-separated list of doubles>]\n"
<< "[--prometheus-response-delay-seconds-histogram-boundaries <comma-separated list of doubles>]\n"
<< " Bucket boundaries for response delay seconds histogram; no boundaries are defined by default.\n"
<< " Scientific notation is allowed, so in terms of microseconds (e-6) and milliseconds (e-3) we\n"
<< " could provide, for example: \"100e-6 200e-6 300e-6 400e-6 500e-6 1e-3 5e-3 10e-3 20e-3\".\n\n"
<< " Scientific notation is allowed, i.e.: \"100e-6,200e-6,300e-6,400e-6,1e-3,5e-3,10e-3,20e-3\".\n\n"

<< "[--prometheus-message-size-bytes-histogram-boundaries <space-separated list of doubles>]\n"
<< "[--prometheus-message-size-bytes-histogram-boundaries <comma-separated list of doubles>]\n"
<< " Bucket boundaries for Tx/Rx message size bytes histogram; no boundaries are defined by default.\n\n"

<< "[-h|--help]\n"
Expand Down Expand Up @@ -309,22 +308,36 @@ void replaceVariables(std::string &str, const std::map<std::string, std::string>
}
}

// Transform input in the form "<double> <double> .. <double>" to bucket boundaries vector
// Cientific notation is allowed, for example boundaries for 150us would be 150e-6
// Transform input in the form "<double>,<double>,..,<double>" to bucket boundaries vector
// Cientific notation is allowed, for example boundary for 150us would be 150e-6
// Returns the final string ignoring non-double values scanned. Also sort is applied.
std::string loadHistogramBoundaries(const std::string &input, ert::metrics::bucket_boundaries_t &boundaries) {
std::string result;

std::stringstream ss(input);
double value = 0;
while (ss >> value) {
boundaries.push_back(value);
std::istringstream ss(input);
std::string item;

while (std::getline(ss, item, ',')) {
try {
double value = std::stod(item);
if (value >= 0) {
boundaries.push_back(value);
result += (std::to_string(value) + ",");
}
else {
std::cerr << "Ignoring negative double: " << item << "\n";
}
} catch (const std::invalid_argument& e) {
std::cerr << "Ignoring invalid double: " << item << "\n";
} catch (const std::out_of_range& e) {
std::cerr << "Ignoring out-of-range double: " << item << "\n";
}
}

// Sort surviving numbers:
std::sort(boundaries.begin(), boundaries.end());
for (const auto &i: boundaries) {
result += (std::to_string(i) + " ");
}

result.pop_back(); // remove last comma

return result;
}
Expand Down Expand Up @@ -824,7 +837,7 @@ int main(int argc, char* argv[])
}

/*
// WITHOUT DELAY FEATURE:
// WITHOUT DELAY FEATURE (also this could cause submit error due to method/path/body not protected in this thread):
boost::asio::post(io_ctx, [&]() {
auto stream = std::make_shared<Stream>(udpData);
stream->setRequest(client, method, path, body, headers, millisecondsTimeout);
Expand Down

0 comments on commit 7cbdda8

Please sign in to comment.