Skip to content

Commit

Permalink
add support for multiple arbitrary commands (#72)
Browse files Browse the repository at this point in the history
* add support for multiple arbitrary commands

this commit expand the ability of running arbitrary command
to enable runing multiple arbitrary commands by
adding the --command option for each command.

* add support for multiple arbitrary commands

cr fixes + adding command line usage
  • Loading branch information
YaacovHazan authored and yaacovhazan-Redislabs committed Apr 29, 2019
1 parent 57c8fdc commit 7cdbaff
Show file tree
Hide file tree
Showing 17 changed files with 889 additions and 420 deletions.
27 changes: 21 additions & 6 deletions bash-completion/memtier_benchmark
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,30 @@ _memtier_completions()
"--requests" "--threads" "--test-time" "--ratio" "--pipeline" "--data-size" "--data-offset"\
"--data-size-range" "--data-size-list" "--expiry-range" "--data-import" "--key-prefix"\
"--key-minimum" "--key-maximum" "--reconnect-interval" "--multi-key-get" "--authenticate"\
"--select-db" "--wait-ratio" "--num-slaves" "--wait-timeout" "--json-out-file" "--command"\
"-s" "-p" "-S" "-o" "-x" "-c" "-n" "-t" "-d" "-a")
"--select-db" "--wait-ratio" "--num-slaves" "--wait-timeout" "--json-out-file"\
"--command" "--command-ratio" "-s" "-p" "-S" "-o" "-x" "-c" "-n" "-t" "-d" "-a")

options_no_args=("--debug" "--show-config" "--hide-histogram" "--distinct-client-seed" "--randomize"\
"--random-data" "--data-verify" "--verify-only" "--generate-keys" "--key-stddev"\
"--key-median" "--no-expiry" "--cluster-mode" "--help" "--version"\
"-D" "-R" "-h" "-v")

options_comp=("--protocol" "-P" "--key-pattern" "--data-size-pattern")
options_comp=("--protocol" "-P" "--key-pattern" "--data-size-pattern" "--command-key-pattern")

all_options="${options_no_comp[@]} ${options_no_args[@]} ${options_comp[@]}"

local cur
local prev
_get_comp_words_by_ref -n ${COMP_WORDBREAKS} cur prev
local cur
local cur_line=${COMP_LINE:0:${COMP_POINT}}
local args_array=(${cur_line})

if [[ "${cur_line}" =~ .*[[:space:]]$ ]]; then
cur=""
prev="${args_array[-1]}"
else
cur="${args_array[-1]}"
prev="${args_array[-2]}"
fi

# check if it's option without completion
local option=$(_memtier_look_for_element "${prev}" "" "${options_no_comp[@]}")
Expand Down Expand Up @@ -65,6 +74,12 @@ _memtier_completions()
"--data-size-pattern")
all_options="R S"
;;
"--command-key-pattern=")
cur=${cur#"--data-size-pattern="}
;&
"--command-key-pattern")
all_options="G R S P"
;;
"--key-pattern=")
cur=${cur#"--key-pattern="}
;&
Expand All @@ -73,7 +88,7 @@ _memtier_completions()
COMPREPLY=( $( compgen -W "G R S P" ) )
else
if [[ "${cur}" =~ (G|R|S|P):(G|R|S)$ ]]; then
COMPREPLY="${COMP_WORDS[COMP_CWORD]} "
COMPREPLY="${cur: -1} "
elif [[ "${cur}" =~ (G|R|S|P):$ ]]; then
COMPREPLY=( $( compgen -W "G R S" ) )
elif [[ "${cur}" =~ (G|R|S|P)$ ]]; then
Expand Down
88 changes: 52 additions & 36 deletions client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ bool client::setup_client(benchmark_config *config, abstract_protocol *protocol,
else if (config->distinct_client_seed)
m_obj_gen->set_random_seed(config->next_client_idx);

if (config->key_pattern[key_pattern_set]=='P') {
// Parallel key-pattern determined according to the first command
if ((config->arbitrary_commands->is_defined() && config->arbitrary_commands->at(0).key_pattern == 'P') ||
(config->key_pattern[key_pattern_set]=='P')) {
unsigned long long total_num_of_clients = config->clients*config->threads;
unsigned long long client_index = config->next_client_idx % total_num_of_clients;

Expand All @@ -95,8 +97,9 @@ bool client::setup_client(benchmark_config *config, abstract_protocol *protocol,

client::client(client_group* group) :
m_event_base(NULL), m_initialized(false), m_end_set(false), m_config(NULL),
m_obj_gen(NULL), m_reqs_processed(0), m_reqs_generated(0),
m_obj_gen(NULL), m_stats(group->get_config()), m_reqs_processed(0), m_reqs_generated(0),
m_set_ratio_count(0), m_get_ratio_count(0),
m_arbitrary_command_ratio_count(0), m_executed_command_index(0),
m_tot_set_ops(0), m_tot_wait_ops(0)
{
m_event_base = group->get_event_base();
Expand All @@ -112,8 +115,9 @@ client::client(client_group* group) :
client::client(struct event_base *event_base, benchmark_config *config,
abstract_protocol *protocol, object_generator *obj_gen) :
m_event_base(NULL), m_initialized(false), m_end_set(false), m_config(NULL),
m_obj_gen(NULL), m_reqs_processed(0), m_reqs_generated(0),
m_obj_gen(NULL), m_stats(config), m_reqs_processed(0), m_reqs_generated(0),
m_set_ratio_count(0), m_get_ratio_count(0),
m_arbitrary_command_ratio_count(0), m_executed_command_index(0),
m_tot_set_ops(0), m_tot_wait_ops(0), m_keylist(NULL)
{
m_event_base = event_base;
Expand Down Expand Up @@ -235,39 +239,49 @@ bool client::hold_pipeline(unsigned int conn_id) {
return false;
}

// This function could use some urgent TLC -- but we need to do it without altering the behavior
void client::create_request(struct timeval timestamp, unsigned int conn_id)
{
// are we using arbitrary command?
if (m_config->command) {
int cmd_size = 0;
for (unsigned int i = 0; i < m_config->command->command_args.size(); i++) {
command_arg* arg = &m_config->command->command_args[i];

if (arg->type == const_type) {
cmd_size += m_connections[conn_id]->send_arbitrary_command(arg);
} else if (arg->type == key_type) {
int iter = obj_iter_type(m_config, 0);
unsigned int key_len;
const char *key = m_obj_gen->get_key(iter, &key_len);
void client::create_arbitrary_request(const arbitrary_command* cmd, struct timeval& timestamp, unsigned int conn_id) {
int cmd_size = 0;

assert(key != NULL);
assert(key_len > 0);
benchmark_debug_log("%s [%s]:\n", cmd->command_name.c_str(), cmd->command.c_str());

cmd_size += m_connections[conn_id]->send_arbitrary_command(arg, key, key_len);
} else if (arg->type == data_type) {
unsigned int value_len;
const char *value = m_obj_gen->get_value(0, &value_len);
for (unsigned int i = 0; i < cmd->command_args.size(); i++) {
const command_arg* arg = &cmd->command_args[i];

assert(value != NULL);
assert(value_len > 0);
if (arg->type == const_type) {
cmd_size += m_connections[conn_id]->send_arbitrary_command(arg);
} else if (arg->type == key_type) {
int iter = get_arbitrary_obj_iter_type(cmd, m_executed_command_index);
unsigned int key_len;
const char *key = m_obj_gen->get_key(iter, &key_len);

cmd_size += m_connections[conn_id]->send_arbitrary_command(arg, value, value_len);
}
assert(key != NULL);
assert(key_len > 0);

cmd_size += m_connections[conn_id]->send_arbitrary_command(arg, key, key_len);
} else if (arg->type == data_type) {
unsigned int value_len;
const char *value = m_obj_gen->get_value(0, &value_len);

assert(value != NULL);
assert(value_len > 0);

cmd_size += m_connections[conn_id]->send_arbitrary_command(arg, value, value_len);
}
}

m_connections[conn_id]->send_arbitrary_command_end(m_executed_command_index, &timestamp, cmd_size);
m_reqs_generated++;
}

// This function could use some urgent TLC -- but we need to do it without altering the behavior
void client::create_request(struct timeval timestamp, unsigned int conn_id)
{
// are we using arbitrary command?
if (m_config->arbitrary_commands->is_defined()) {
const arbitrary_command* executed_command = m_config->arbitrary_commands->get_next_executed_command(m_arbitrary_command_ratio_count,
m_executed_command_index);
create_arbitrary_request(executed_command, timestamp, conn_id);

m_connections[conn_id]->send_arbitrary_command_end(&timestamp, cmd_size);
m_reqs_generated++;
return;
}

Expand Down Expand Up @@ -382,11 +396,14 @@ void client::handle_response(unsigned int conn_id, struct timeval timestamp,
m_stats.update_wait_op(&timestamp,
ts_diff(request->m_sent_time, timestamp));
break;
case rt_arbitrary:
m_stats.update_aribitrary_op(&timestamp,
request->m_size + response->get_total_len(),
ts_diff(request->m_sent_time, timestamp));
case rt_arbitrary: {
arbitrary_request *ar = static_cast<arbitrary_request *>(request);
m_stats.update_arbitrary_op(&timestamp,
request->m_size + response->get_total_len(),
ts_diff(request->m_sent_time, timestamp),
ar->index);
break;
}
default:
assert(0);
break;
Expand Down Expand Up @@ -629,8 +646,7 @@ void client_group::write_client_stats(const char *prefix)
char filename[PATH_MAX];

snprintf(filename, sizeof(filename)-1, "%s-%u.csv", prefix, client_id++);
if (!(*i)->get_stats()->save_csv(filename, m_config->cluster_mode,
m_config->command ? m_config->command->command_name : "")) {
if (!(*i)->get_stats()->save_csv(filename, m_config)) {
fprintf(stderr, "error: %s: failed to write client stats.\n", filename);
}
}
Expand Down
13 changes: 13 additions & 0 deletions client.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ class client : public connections_manager {
unsigned long long m_reqs_generated; // requests generated (wait for responses)
unsigned int m_set_ratio_count; // number of sets counter (overlaps on ratio)
unsigned int m_get_ratio_count; // number of gets counter (overlaps on ratio)
unsigned int m_arbitrary_command_ratio_count; // number of arbitrary commands counter (overlaps on ratio)
unsigned int m_executed_command_index; // current arbitrary command executed

unsigned long long m_tot_set_ops; // Total number of SET ops
unsigned long long m_tot_wait_ops; // Total number of WAIT ops
Expand Down Expand Up @@ -108,6 +110,7 @@ class client : public connections_manager {
virtual bool finished(void);
virtual void set_start_time();
virtual void set_end_time();
virtual void create_arbitrary_request(const arbitrary_command* cmd, struct timeval& timestamp, unsigned int conn_id);
virtual void create_request(struct timeval timestamp, unsigned int conn_id);
virtual bool hold_pipeline(unsigned int conn_id);
virtual int connect(void);
Expand All @@ -128,6 +131,16 @@ class client : public connections_manager {
return OBJECT_GENERATOR_KEY_GET_ITER;
}
}

inline int get_arbitrary_obj_iter_type(const arbitrary_command* cmd, unsigned int index) {
if (cmd->key_pattern == 'R') {
return OBJECT_GENERATOR_KEY_RANDOM;
} else if (cmd->key_pattern == 'G') {
return OBJECT_GENERATOR_KEY_GAUSSIAN;
} else {
return index;
}
}
};

class verify_client : public client {
Expand Down
47 changes: 44 additions & 3 deletions config_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <errno.h>

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
Expand All @@ -35,6 +36,8 @@

#include <string>
#include <stdexcept>
#include <climits>
#include <algorithm>

#include "config_types.h"

Expand Down Expand Up @@ -276,9 +279,47 @@ static int hex_digit_to_int(char c) {
}
}

bool arbitrary_command::split_command_to_args(const char* command) {
const char *p = command;
size_t command_len = strlen(command);
arbitrary_command::arbitrary_command(const char* cmd) : command(cmd), key_pattern('R'), ratio(1) {
// command name is the first word in the command
size_t pos = command.find(" ");
if (pos == std::string::npos) {
pos = command.size();
}

command_name.assign(command.c_str(), pos);
std::transform(command_name.begin(), command_name.end(), command_name.begin(), ::toupper);
}

bool arbitrary_command::set_key_pattern(const char* pattern_str) {
if (strlen(pattern_str) > 1) {
return false;
}

if (pattern_str[0] != 'R' &&
pattern_str[0] != 'G' &&
pattern_str[0] != 'S' &&
pattern_str[0] != 'P') {

return false;
}

key_pattern = pattern_str[0];
return true;
}

bool arbitrary_command::set_ratio(const char* ratio_str) {
char *q = NULL;
ratio = strtoul(ratio_str, &q, 10);
if (!q || *q != '\0') {
return false;
}

return true;
}

bool arbitrary_command::split_command_to_args() {
const char *p = command.c_str();
size_t command_len = command.length();

char buffer[command_len];
unsigned int buffer_len = 0;
Expand Down
70 changes: 69 additions & 1 deletion config_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,77 @@ struct command_arg {
};

struct arbitrary_command {
arbitrary_command(const char* cmd);

bool set_key_pattern(const char* pattern_str);
bool set_ratio(const char* pattern_str);
bool split_command_to_args();

std::vector<command_arg> command_args;
std::string command;
std::string command_name;
bool split_command_to_args(const char* command);
char key_pattern;
unsigned int ratio;
};

class arbitrary_command_list {
private:
std::vector<arbitrary_command> commands_list;

public:
arbitrary_command_list() {;}

arbitrary_command& at(size_t idx) { return commands_list.at(idx); }
const arbitrary_command& at(std::size_t idx) const { return commands_list.at(idx); }

// array subscript operator
arbitrary_command& operator[](std::size_t idx) { return commands_list[idx]; }
const arbitrary_command& operator[](std::size_t idx) const { return commands_list[idx]; }

void add_command(const arbitrary_command& command) {
commands_list.push_back(command);
}

arbitrary_command& get_last_command() {
return commands_list.back();
}

size_t size() const {
return commands_list.size();
}

bool is_defined() const {
return !commands_list.empty();
}

const arbitrary_command* get_next_executed_command(unsigned int& ratio_count, unsigned int& executed_command_index) const {
while(true) {
const arbitrary_command* executed_command = &commands_list[executed_command_index];

if (ratio_count < executed_command->ratio) {
ratio_count++;
return executed_command;
} else {
ratio_count = 0;
executed_command_index++;
if (executed_command_index == size()) {
executed_command_index = 0;
}
}
}
}

unsigned int get_max_command_name_length() const {
unsigned int max_length = 0;

for (size_t i=0; i<size(); i++) {
if (commands_list[i].command_name.length() > max_length) {
max_length = commands_list[i].command_name.length();
}
}

return max_length;
}
};

#endif /* _CONFIG_TYPES_H */
Loading

0 comments on commit 7cdbaff

Please sign in to comment.