From 46a269c9a7c4812387d36cead7eb5178bca9a549 Mon Sep 17 00:00:00 2001 From: Nik Sultana Date: Fri, 13 May 2016 20:41:14 +0100 Subject: [PATCH 1/7] committed changes from @marcinwoj; --- client.cpp | 30 +++++++++++++++++++++++++++--- config_types.cpp | 3 ++- libmemcached_protocol/binary.h | 2 ++ obj_gen.cpp | 18 ++++++++++++++++-- protocol.cpp | 6 +++++- 5 files changed, 52 insertions(+), 7 deletions(-) diff --git a/client.cpp b/client.cpp index 06912aea..94b24e53 100755 --- a/client.cpp +++ b/client.cpp @@ -89,6 +89,18 @@ inline unsigned long int ts_diff_now(struct timeval a) return bval - aval; } +/*inline unsigned long int ts_now() +{ + struct timeval b; + + gettimeofday(&b, NULL); + //unsigned long long bval = b.tv_sec * 1000000 + b.tv_usec; + + //return bval; + return b; +} +*/ + inline timeval timeval_factorial_avarge( timeval a, timeval b, unsigned int weight) { timeval tv; @@ -275,7 +287,8 @@ int client::connect(void) evbuffer_drain(m_write_buf, evbuffer_get_length(m_write_buf)); if (m_unix_sockaddr != NULL) { - m_sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + //m_sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + m_sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); if (m_sockfd < 0) { return -errno; } @@ -300,8 +313,8 @@ int client::connect(void) error = setsockopt(m_sockfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling)); assert(error == 0); - error = setsockopt(m_sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); - assert(error == 0); + //error = setsockopt(m_sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); + //assert(error == 0); } // set non-blcoking behavior @@ -555,6 +568,7 @@ void client::create_request(void) assert(key != NULL); assert(keylen > 0); + keylen = 6; benchmark_debug_log("GET key=[%.*s]\n", keylen, key); cmd_size = m_protocol->write_command_get(key, keylen, m_config->data_offset); @@ -616,15 +630,24 @@ void client::process_first_request(void) void client::handle_response(request *request, protocol_response *response) { + + //long long unsigned int tmp = 0; switch (request->m_type) { case rt_get: + //printf("GET %lu\n",ts_diff_now(request->m_sent_time)); m_stats.update_get_op(NULL, request->m_size + response->get_total_len(), ts_diff_now(request->m_sent_time), response->get_hits(), request->m_keys - response->get_hits()); + //printf("now %d\n",ts_now()); + //b.tv_sec * 1000000 + b.tv_usec; + //printf("request->m_sent_time %lld %lld\n",request->m_sent_time.tv_sec, request->m_sent_time.tv_usec); + //printf("Latency %d\n",ts_diff_now(request->m_sent_time)); + //printf("Get %lld\n",ts_diff_now(request->m_sent_time)); break; case rt_set: + // printf("SET %lu\n",ts_diff_now(request->m_sent_time)); m_stats.update_set_op(NULL, request->m_size + response->get_total_len(), ts_diff_now(request->m_sent_time)); @@ -1380,6 +1403,7 @@ void run_stats::print(FILE *out, bool histogram) // aggregate all one_second_stats; we do this only if we have // one_second_stats, otherwise it means we're probably printing previously // aggregated data + debug_dump(); if (m_stats.size() > 0) { summarize(m_totals); } diff --git a/config_types.cpp b/config_types.cpp index 8c098404..42cf3149 100644 --- a/config_types.cpp +++ b/config_types.cpp @@ -215,7 +215,8 @@ int server_addr::resolve(void) memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; - hints.ai_socktype = SOCK_STREAM; + //hints.ai_socktype = SOCK_STREAM; + hints.ai_socktype = SOCK_DGRAM; hints.ai_family = AF_INET; // Don't play with IPv6 for now... snprintf(port_str, sizeof(port_str)-1, "%u", m_port); diff --git a/libmemcached_protocol/binary.h b/libmemcached_protocol/binary.h index 7cd313ec..0b6eddc2 100644 --- a/libmemcached_protocol/binary.h +++ b/libmemcached_protocol/binary.h @@ -185,6 +185,7 @@ extern "C" */ typedef union { struct { + uint64_t udp_header; uint8_t magic; uint8_t opcode; uint16_t keylen; @@ -204,6 +205,7 @@ extern "C" */ typedef union { struct { + uint64_t udp_header; uint8_t magic; uint8_t opcode; uint16_t keylen; diff --git a/obj_gen.cpp b/obj_gen.cpp index 94e39ea2..9a5e5232 100644 --- a/obj_gen.cpp +++ b/obj_gen.cpp @@ -358,12 +358,24 @@ unsigned int object_generator::get_key_index(int iter) const char* object_generator::get_key(int iter, unsigned int *len) { + + char a[6] = {0xC, 0xA, 0xF, 0xE, 0xC, 0xA}; + unsigned int i; unsigned int l; m_key_index = get_key_index(iter); // format key - l = snprintf(m_key_buffer, sizeof(m_key_buffer)-1, - "%s%u", m_key_prefix, m_key_index); + //l = snprintf(m_key_buffer, sizeof(m_key_buffer)-1, + + //fixed + //l = snprintf(m_key_buffer, 7, + // "%s%u", m_key_prefix, m_key_index); + + //rnd COMENT OUT for FIX key + for (i=0; i < 6; ++i){ + a[i] = (char)random_range(0, 15); + } + l = snprintf(m_key_buffer, 7, "%x%x%x%x%x%x%x%x", a[0], a[1], a[2], a[3], a[4], a[5], a[0], a[1]); if (len != NULL) *len = l; return m_key_buffer; @@ -400,6 +412,8 @@ data_object* object_generator::get_object(int iter) } // set object + new_size = 8; + //printf("setting key of len %d\n", strlen(m_key_buffer)); m_object.set_key(m_key_buffer, strlen(m_key_buffer)); m_object.set_value(m_value_buffer, new_size); m_object.set_expiry(expiry); diff --git a/protocol.cpp b/protocol.cpp index f6e6c23e..10b47f31 100644 --- a/protocol.cpp +++ b/protocol.cpp @@ -410,7 +410,7 @@ int memcache_text_protocol::write_command_set(const char *key, int key_len, cons assert(value != NULL); assert(value_len > 0); int size = 0; - + size = evbuffer_add_printf(m_write_buf, "set %.*s 0 %u %u\r\n", key_len, key, expiry, value_len); evbuffer_add(m_write_buf, value, value_len); @@ -630,6 +630,7 @@ int memcache_binary_protocol::write_command_set(const char *key, int key_len, co protocol_binary_request_set req; memset(&req, 0, sizeof(req)); + req.message.header.request.udp_header = 0x0000010000000000; req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_SET; req.message.header.request.keylen = htons(key_len); @@ -653,6 +654,8 @@ int memcache_binary_protocol::write_command_get(const char *key, int key_len, un protocol_binary_request_get req; memset(&req, 0, sizeof(req)); + benchmark_debug_log("here"); + req.message.header.request.udp_header = 0x0000010000000000; req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_GET; req.message.header.request.keylen = htons(key_len); @@ -724,6 +727,7 @@ int memcache_binary_protocol::parse_response(void) ret = evbuffer_remove(m_read_buf, (void *)&m_response_hdr, sizeof(m_response_hdr)); assert(ret == sizeof(m_response_hdr)); + //printf("sizse of resposne header %d\n",sizeof(m_response_hdr)); if (m_response_hdr.message.header.response.magic != PROTOCOL_BINARY_RES) { benchmark_error_log("error: invalid memcache response header magic.\n"); From d8cfb793e8f7dbee39dcde0bf2617b77ae7d94da Mon Sep 17 00:00:00 2001 From: Nik Sultana Date: Wed, 25 May 2016 18:57:35 +0100 Subject: [PATCH 2/7] removed commented code used during development of 46a269c9a7c4812387d36cead7eb5178bca9a549 and marked out bits that need improving; --- client.cpp | 29 +++++------------------------ config_types.cpp | 2 +- libmemcached_protocol/binary.h | 4 ++-- obj_gen.cpp | 5 ++--- protocol.cpp | 6 ++---- 5 files changed, 12 insertions(+), 34 deletions(-) diff --git a/client.cpp b/client.cpp index 94b24e53..36f1e837 100755 --- a/client.cpp +++ b/client.cpp @@ -89,18 +89,6 @@ inline unsigned long int ts_diff_now(struct timeval a) return bval - aval; } -/*inline unsigned long int ts_now() -{ - struct timeval b; - - gettimeofday(&b, NULL); - //unsigned long long bval = b.tv_sec * 1000000 + b.tv_usec; - - //return bval; - return b; -} -*/ - inline timeval timeval_factorial_avarge( timeval a, timeval b, unsigned int weight) { timeval tv; @@ -288,7 +276,7 @@ int client::connect(void) if (m_unix_sockaddr != NULL) { //m_sockfd = socket(AF_UNIX, SOCK_STREAM, 0); - m_sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); + m_sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); // FIXME UDP adaptation if (m_sockfd < 0) { return -errno; } @@ -313,6 +301,7 @@ int client::connect(void) error = setsockopt(m_sockfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling)); assert(error == 0); + // FIXME UDP adaptation //error = setsockopt(m_sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); //assert(error == 0); } @@ -568,7 +557,7 @@ void client::create_request(void) assert(key != NULL); assert(keylen > 0); - keylen = 6; + keylen = 6; // FIXME const benchmark_debug_log("GET key=[%.*s]\n", keylen, key); cmd_size = m_protocol->write_command_get(key, keylen, m_config->data_offset); @@ -630,24 +619,17 @@ void client::process_first_request(void) void client::handle_response(request *request, protocol_response *response) { - - //long long unsigned int tmp = 0; switch (request->m_type) { case rt_get: - //printf("GET %lu\n",ts_diff_now(request->m_sent_time)); + //printf("GET %lu\n",ts_diff_now(request->m_sent_time)); // FIXME latency printf m_stats.update_get_op(NULL, request->m_size + response->get_total_len(), ts_diff_now(request->m_sent_time), response->get_hits(), request->m_keys - response->get_hits()); - //printf("now %d\n",ts_now()); - //b.tv_sec * 1000000 + b.tv_usec; - //printf("request->m_sent_time %lld %lld\n",request->m_sent_time.tv_sec, request->m_sent_time.tv_usec); - //printf("Latency %d\n",ts_diff_now(request->m_sent_time)); - //printf("Get %lld\n",ts_diff_now(request->m_sent_time)); break; case rt_set: - // printf("SET %lu\n",ts_diff_now(request->m_sent_time)); + // printf("SET %lu\n",ts_diff_now(request->m_sent_time)); // FIXME latency printf m_stats.update_set_op(NULL, request->m_size + response->get_total_len(), ts_diff_now(request->m_sent_time)); @@ -1403,7 +1385,6 @@ void run_stats::print(FILE *out, bool histogram) // aggregate all one_second_stats; we do this only if we have // one_second_stats, otherwise it means we're probably printing previously // aggregated data - debug_dump(); if (m_stats.size() > 0) { summarize(m_totals); } diff --git a/config_types.cpp b/config_types.cpp index 42cf3149..62c7769c 100644 --- a/config_types.cpp +++ b/config_types.cpp @@ -215,7 +215,7 @@ int server_addr::resolve(void) memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; - //hints.ai_socktype = SOCK_STREAM; + //hints.ai_socktype = SOCK_STREAM; // FIXME UDP adaptation hints.ai_socktype = SOCK_DGRAM; hints.ai_family = AF_INET; // Don't play with IPv6 for now... diff --git a/libmemcached_protocol/binary.h b/libmemcached_protocol/binary.h index 0b6eddc2..0bb65fa3 100644 --- a/libmemcached_protocol/binary.h +++ b/libmemcached_protocol/binary.h @@ -185,7 +185,7 @@ extern "C" */ typedef union { struct { - uint64_t udp_header; + uint64_t udp_header; // FIXME UDP adaptation uint8_t magic; uint8_t opcode; uint16_t keylen; @@ -205,7 +205,7 @@ extern "C" */ typedef union { struct { - uint64_t udp_header; + uint64_t udp_header; // FIXME UDP adaptation uint8_t magic; uint8_t opcode; uint16_t keylen; diff --git a/obj_gen.cpp b/obj_gen.cpp index 9a5e5232..6d593372 100644 --- a/obj_gen.cpp +++ b/obj_gen.cpp @@ -356,9 +356,9 @@ unsigned int object_generator::get_key_index(int iter) return k; } +// FIXME various additions to generate fixed-size keys of two kinds: random, and fixed contents. const char* object_generator::get_key(int iter, unsigned int *len) { - char a[6] = {0xC, 0xA, 0xF, 0xE, 0xC, 0xA}; unsigned int i; unsigned int l; @@ -412,8 +412,7 @@ data_object* object_generator::get_object(int iter) } // set object - new_size = 8; - //printf("setting key of len %d\n", strlen(m_key_buffer)); + new_size = 8; // FIXME const m_object.set_key(m_key_buffer, strlen(m_key_buffer)); m_object.set_value(m_value_buffer, new_size); m_object.set_expiry(expiry); diff --git a/protocol.cpp b/protocol.cpp index 10b47f31..78ac00ca 100644 --- a/protocol.cpp +++ b/protocol.cpp @@ -630,7 +630,7 @@ int memcache_binary_protocol::write_command_set(const char *key, int key_len, co protocol_binary_request_set req; memset(&req, 0, sizeof(req)); - req.message.header.request.udp_header = 0x0000010000000000; + req.message.header.request.udp_header = 0x0000010000000000; //FIXME fudge req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_SET; req.message.header.request.keylen = htons(key_len); @@ -654,8 +654,7 @@ int memcache_binary_protocol::write_command_get(const char *key, int key_len, un protocol_binary_request_get req; memset(&req, 0, sizeof(req)); - benchmark_debug_log("here"); - req.message.header.request.udp_header = 0x0000010000000000; + req.message.header.request.udp_header = 0x0000010000000000; //FIXME fudge req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_GET; req.message.header.request.keylen = htons(key_len); @@ -727,7 +726,6 @@ int memcache_binary_protocol::parse_response(void) ret = evbuffer_remove(m_read_buf, (void *)&m_response_hdr, sizeof(m_response_hdr)); assert(ret == sizeof(m_response_hdr)); - //printf("sizse of resposne header %d\n",sizeof(m_response_hdr)); if (m_response_hdr.message.header.response.magic != PROTOCOL_BINARY_RES) { benchmark_error_log("error: invalid memcache response header magic.\n"); From 31348abebfee3f5132d6071e18c85246c973c3fe Mon Sep 17 00:00:00 2001 From: Nik Sultana Date: Thu, 26 May 2016 16:58:15 +0100 Subject: [PATCH 3/7] added '--transaction_latency' flag to print out the raw latency of each transaction; --- client.cpp | 14 ++++++++++++-- memtier_benchmark.cpp | 14 +++++++++++--- memtier_benchmark.h | 1 + 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/client.cpp b/client.cpp index 36f1e837..e084d00d 100755 --- a/client.cpp +++ b/client.cpp @@ -621,7 +621,12 @@ void client::handle_response(request *request, protocol_response *response) { switch (request->m_type) { case rt_get: - //printf("GET %lu\n",ts_diff_now(request->m_sent_time)); // FIXME latency printf + if (m_config->transaction_latency) { + // NOTE using printf adds latency to the client because of the system call, but we're measuring transaction latency, not throughput. + // FIXME might be preferable to print to some user-specified file, rather than to stdout + printf("GET %lu\n", ts_diff_now(request->m_sent_time)); + } + m_stats.update_get_op(NULL, request->m_size + response->get_total_len(), ts_diff_now(request->m_sent_time), @@ -629,7 +634,12 @@ void client::handle_response(request *request, protocol_response *response) request->m_keys - response->get_hits()); break; case rt_set: - // printf("SET %lu\n",ts_diff_now(request->m_sent_time)); // FIXME latency printf + if (m_config->transaction_latency) { + // NOTE using printf adds latency to the client because of the system call, but we're measuring transaction latency, not throughput. + // FIXME might be preferable to print to some user-specified file, rather than to stdout + printf("SET %lu\n", ts_diff_now(request->m_sent_time)); + } + m_stats.update_set_op(NULL, request->m_size + response->get_total_len(), ts_diff_now(request->m_sent_time)); diff --git a/memtier_benchmark.cpp b/memtier_benchmark.cpp index f32ad1d2..3df25221 100755 --- a/memtier_benchmark.cpp +++ b/memtier_benchmark.cpp @@ -119,7 +119,8 @@ static void config_print(FILE *file, struct benchmark_config *cfg) "multi_key_get = %u\n" "authenticate = %s\n" "select-db = %d\n" - "no-expiry = %s\n", + "no-expiry = %s\n" + "transaction_latency = %s\n", cfg->server, cfg->port, cfg->unix_socket, @@ -155,7 +156,8 @@ static void config_print(FILE *file, struct benchmark_config *cfg) cfg->multi_key_get, cfg->authenticate ? cfg->authenticate : "", cfg->select_db, - cfg->no_expiry ? "yes" : "no"); + cfg->no_expiry ? "yes" : "no", + cfg->transaction_latency ? "yes" : "no"); } static void config_init_defaults(struct benchmark_config *cfg) @@ -241,7 +243,8 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf o_generate_keys, o_multi_key_get, o_select_db, - o_no_expiry + o_no_expiry, + o_transaction_latency }; static struct option long_options[] = { @@ -287,6 +290,7 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf { "no-expiry", 0, 0, o_no_expiry }, { "help", 0, 0, 'h' }, { "version", 0, 0, 'v' }, + { "transaction_latency", 0, 0, o_transaction_latency }, { NULL, 0, 0, 0 } }; @@ -553,6 +557,9 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf case o_no_expiry: cfg->no_expiry = true; break; + case o_transaction_latency: + cfg->transaction_latency = true; + break; default: return -1; break; @@ -597,6 +604,7 @@ void usage() { " --select-db=DB DB number to select, when testing a redis server\n" " --distinct-client-seed Use a different random seed for each client\n" " --randomize random seed based on timestamp (default is constant value)\n" + " --transaction_latency Measure and report the latency of each transaction\n" "\n" "Object Options:\n" " -d --data-size=SIZE Object data size (default: 32)\n" diff --git a/memtier_benchmark.h b/memtier_benchmark.h index abf112a9..8e812da7 100644 --- a/memtier_benchmark.h +++ b/memtier_benchmark.h @@ -75,6 +75,7 @@ struct benchmark_config { int select_db; bool no_expiry; bool resolve_on_connect; + bool transaction_latency; }; From 08cef2e24d794d4323ebb1a15223501f73e2df3c Mon Sep 17 00:00:00 2001 From: Nik Sultana Date: Thu, 26 May 2016 17:16:23 +0100 Subject: [PATCH 4/7] removed hard-coded value size, since we can get the same effect by using '-d 8'; --- obj_gen.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/obj_gen.cpp b/obj_gen.cpp index 6d593372..3d79b728 100644 --- a/obj_gen.cpp +++ b/obj_gen.cpp @@ -412,7 +412,6 @@ data_object* object_generator::get_object(int iter) } // set object - new_size = 8; // FIXME const m_object.set_key(m_key_buffer, strlen(m_key_buffer)); m_object.set_value(m_value_buffer, new_size); m_object.set_expiry(expiry); From 6995310d698a835e82ac776438e8d35c178eab86 Mon Sep 17 00:00:00 2001 From: Nik Sultana Date: Fri, 27 May 2016 19:22:35 +0100 Subject: [PATCH 5/7] added --key-width parameter to set the maximum width of the key in bytes. Added checks to ensure that the prefix and maximum key index will fit in this width, and that the width does not exceed the current maximum (250 bytes); --- memtier_benchmark.cpp | 25 ++++++++++++++++--- memtier_benchmark.h | 1 + obj_gen.cpp | 58 +++++++++++++++++++++++++++++++------------ obj_gen.h | 6 ++++- 4 files changed, 69 insertions(+), 21 deletions(-) diff --git a/memtier_benchmark.cpp b/memtier_benchmark.cpp index 3df25221..acff8d3f 100755 --- a/memtier_benchmark.cpp +++ b/memtier_benchmark.cpp @@ -120,7 +120,8 @@ static void config_print(FILE *file, struct benchmark_config *cfg) "authenticate = %s\n" "select-db = %d\n" "no-expiry = %s\n" - "transaction_latency = %s\n", + "transaction_latency = %s\n" + "key-width = %u\n", cfg->server, cfg->port, cfg->unix_socket, @@ -157,7 +158,8 @@ static void config_print(FILE *file, struct benchmark_config *cfg) cfg->authenticate ? cfg->authenticate : "", cfg->select_db, cfg->no_expiry ? "yes" : "no", - cfg->transaction_latency ? "yes" : "no"); + cfg->transaction_latency ? "yes" : "no", + cfg->key_width); } static void config_init_defaults(struct benchmark_config *cfg) @@ -198,6 +200,8 @@ static void config_init_defaults(struct benchmark_config *cfg) } if (!cfg->requests && !cfg->test_time) cfg->requests = 10000; + if (!cfg->key_width) + cfg->key_width = OBJECT_GENERATOR_KEY_WIDTH; } static int generate_random_seed() @@ -244,7 +248,8 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf o_multi_key_get, o_select_db, o_no_expiry, - o_transaction_latency + o_transaction_latency, + o_key_width, }; static struct option long_options[] = { @@ -291,6 +296,7 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf { "help", 0, 0, 'h' }, { "version", 0, 0, 'v' }, { "transaction_latency", 0, 0, o_transaction_latency }, + { "key-width", 1, 0, o_key_width }, { NULL, 0, 0, 0 } }; @@ -559,6 +565,16 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf break; case o_transaction_latency: cfg->transaction_latency = true; + break; + case o_key_width: + endptr = NULL; + cfg->key_width = (unsigned short) strtoul(optarg, &endptr, 10); + if (!cfg->key_width || cfg->key_width > OBJECT_GENERATOR_KEY_WIDTH || !endptr || *endptr != '\0') { + fprintf(stderr, "error: key-width must be a number in the range [1-%u].\n", OBJECT_GENERATOR_KEY_WIDTH); + return -1; + } + break; + break; default: return -1; @@ -627,6 +643,7 @@ void usage() { " --no-expiry Ignore expiry information in imported data\n" "\n" "Key Options:\n" + " --key-width=NUMBER Maximum key size (default: \"250\")\n" " --key-prefix=PREFIX Prefix for keys (default: \"memtier-\")\n" " --key-minimum=NUMBER Key ID minimum value (default: 0)\n" " --key-maximum=NUMBER Key ID maximum value (default: 10000000)\n" @@ -911,7 +928,7 @@ int main(int argc, char *argv[]) exit(1); } - obj_gen = new object_generator(); + obj_gen = new object_generator(cfg.key_width); assert(obj_gen != NULL); } else { // check paramters diff --git a/memtier_benchmark.h b/memtier_benchmark.h index 8e812da7..3e84ce83 100644 --- a/memtier_benchmark.h +++ b/memtier_benchmark.h @@ -76,6 +76,7 @@ struct benchmark_config { bool no_expiry; bool resolve_on_connect; bool transaction_latency; + unsigned short key_width; }; diff --git a/obj_gen.cpp b/obj_gen.cpp index 3d79b728..010bb5b9 100644 --- a/obj_gen.cpp +++ b/obj_gen.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef HAVE_ASSERT_H #include @@ -117,6 +118,7 @@ int gaussian_noise::gaussian_distribution_range(double stddev, double median, in } object_generator::object_generator() : + m_key_width(OBJECT_GENERATOR_KEY_WIDTH), m_data_size_type(data_size_unknown), m_data_size_pattern(NULL), m_random_data(false), @@ -136,7 +138,31 @@ object_generator::object_generator() : m_data_size.size_list = NULL; } +object_generator::object_generator(unsigned int key_width) : + m_key_width(key_width), + m_data_size_type(data_size_unknown), + m_data_size_pattern(NULL), + m_random_data(false), + m_expiry_min(0), + m_expiry_max(0), + m_key_prefix(NULL), + m_key_min(0), + m_key_max(0), + m_key_stddev(0), + m_key_median(0), + m_value_buffer(NULL), + m_random_fd(-1) +{ + assert(m_key_width <= OBJECT_GENERATOR_KEY_WIDTH); + + for (int i = 0; i < OBJECT_GENERATOR_KEY_ITERATORS; i++) + m_next_key[i] = 0; + + m_data_size.size_list = NULL; +} + object_generator::object_generator(const object_generator& copy) : + m_key_width(copy.m_key_width), m_data_size_type(copy.m_data_size_type), m_data_size(copy.m_data_size), m_data_size_pattern(copy.m_data_size_pattern), @@ -150,7 +176,6 @@ object_generator::object_generator(const object_generator& copy) : m_key_median(copy.m_key_median), m_value_buffer(NULL), m_random_fd(-1) - { if (m_data_size_type == data_size_weighted && m_data_size.size_list != NULL) { @@ -304,15 +329,28 @@ void object_generator::set_expiry_range(unsigned int expiry_min, unsigned int ex m_expiry_max = expiry_max; } +void object_generator::check_key_size() +{ + unsigned int width_of_key_prefix = m_key_prefix == NULL ? 0 : strlen(m_key_prefix); + unsigned int width_of_key_max = (unsigned)log10((double)m_key_max) + 1; + if (width_of_key_prefix + width_of_key_max > m_key_width) { + char str [200]; + sprintf(str, "Key prefix '%s' (length %u) exceeds maximum key width (%u) when combined with the maximum key index (%u, length %u)", m_key_prefix, width_of_key_prefix, m_key_width, m_key_max, width_of_key_max); + throw std::logic_error(str); + } +} + void object_generator::set_key_prefix(const char *key_prefix) { m_key_prefix = key_prefix; + check_key_size(); } void object_generator::set_key_range(unsigned int key_min, unsigned int key_max) { m_key_min = key_min; m_key_max = key_max; + check_key_size(); } void object_generator::set_key_distribution(double key_stddev, double key_median) @@ -356,26 +394,14 @@ unsigned int object_generator::get_key_index(int iter) return k; } -// FIXME various additions to generate fixed-size keys of two kinds: random, and fixed contents. const char* object_generator::get_key(int iter, unsigned int *len) { - char a[6] = {0xC, 0xA, 0xF, 0xE, 0xC, 0xA}; - unsigned int i; unsigned int l; m_key_index = get_key_index(iter); - + // format key - //l = snprintf(m_key_buffer, sizeof(m_key_buffer)-1, - - //fixed - //l = snprintf(m_key_buffer, 7, - // "%s%u", m_key_prefix, m_key_index); - - //rnd COMENT OUT for FIX key - for (i=0; i < 6; ++i){ - a[i] = (char)random_range(0, 15); - } - l = snprintf(m_key_buffer, 7, "%x%x%x%x%x%x%x%x", a[0], a[1], a[2], a[3], a[4], a[5], a[0], a[1]); + l = snprintf(m_key_buffer, m_key_width - 1, + "%s%u", m_key_prefix, m_key_index); if (len != NULL) *len = l; return m_key_buffer; diff --git a/obj_gen.h b/obj_gen.h index 8078bbcd..0769058d 100644 --- a/obj_gen.h +++ b/obj_gen.h @@ -75,10 +75,12 @@ class data_object { #define OBJECT_GENERATOR_KEY_GET_ITER 0 #define OBJECT_GENERATOR_KEY_RANDOM -1 #define OBJECT_GENERATOR_KEY_GAUSSIAN -2 +#define OBJECT_GENERATOR_KEY_WIDTH 250 class object_generator { public: enum data_size_type { data_size_unknown, data_size_fixed, data_size_range, data_size_weighted }; + const unsigned int m_key_width; protected: data_size_type m_data_size_type; union { @@ -103,7 +105,7 @@ class object_generator { unsigned int m_next_key[OBJECT_GENERATOR_KEY_ITERATORS]; unsigned int m_key_index; - char m_key_buffer[250]; + char m_key_buffer[OBJECT_GENERATOR_KEY_WIDTH]; char *m_value_buffer; int m_random_fd; gaussian_noise m_random; @@ -116,6 +118,7 @@ class object_generator { unsigned int get_key_index(int iter); public: object_generator(); + object_generator(unsigned int key_width); object_generator(const object_generator& copy); virtual ~object_generator(); virtual object_generator* clone(void); @@ -130,6 +133,7 @@ class object_generator { void set_key_range(unsigned int key_min, unsigned int key_max); void set_key_distribution(double key_stddev, double key_median); void set_random_seed(int seed); + void check_key_size(); virtual const char* get_key(int iter, unsigned int *len); virtual data_object* get_object(int iter); From 05a0593aa88e8bdd49a00b48a1952141b29f7c27 Mon Sep 17 00:00:00 2001 From: Nik Sultana Date: Fri, 27 May 2016 19:23:22 +0100 Subject: [PATCH 6/7] added useful assertion; --- obj_gen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/obj_gen.cpp b/obj_gen.cpp index 010bb5b9..9f4a3de3 100644 --- a/obj_gen.cpp +++ b/obj_gen.cpp @@ -348,6 +348,7 @@ void object_generator::set_key_prefix(const char *key_prefix) void object_generator::set_key_range(unsigned int key_min, unsigned int key_max) { + assert (key_min <= key_max); m_key_min = key_min; m_key_max = key_max; check_key_size(); From 50112424489bac59c3dabea71e5f96dd548bb7ff Mon Sep 17 00:00:00 2001 From: Nik Sultana Date: Sat, 28 May 2016 14:01:52 +0100 Subject: [PATCH 7/7] added --udp parameter to generate traffic over UDP instead of TCP. Only tested with Memcached. When using the binary protocol, a header is inserted between the UDP header and memcached payload; --- client.cpp | 10 ++++---- config_types.cpp | 7 +++--- config_types.h | 4 +++- libmemcached_protocol/binary.h | 18 ++++++++++++-- memtier_benchmark.cpp | 20 +++++++++++----- memtier_benchmark.h | 1 + protocol.cpp | 44 +++++++++++++++++++++++++++------- protocol.h | 2 +- 8 files changed, 79 insertions(+), 27 deletions(-) diff --git a/client.cpp b/client.cpp index e084d00d..64e84fea 100755 --- a/client.cpp +++ b/client.cpp @@ -275,8 +275,7 @@ int client::connect(void) evbuffer_drain(m_write_buf, evbuffer_get_length(m_write_buf)); if (m_unix_sockaddr != NULL) { - //m_sockfd = socket(AF_UNIX, SOCK_STREAM, 0); - m_sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); // FIXME UDP adaptation + m_sockfd = socket(AF_UNIX, m_config->use_udp ? SOCK_DGRAM : SOCK_STREAM, 0); if (m_sockfd < 0) { return -errno; } @@ -301,9 +300,10 @@ int client::connect(void) error = setsockopt(m_sockfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling)); assert(error == 0); - // FIXME UDP adaptation - //error = setsockopt(m_sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); - //assert(error == 0); + if (!m_config->use_udp) { + error = setsockopt(m_sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); + assert(error == 0); + } } // set non-blcoking behavior diff --git a/config_types.cpp b/config_types.cpp index 62c7769c..e95a0d32 100644 --- a/config_types.cpp +++ b/config_types.cpp @@ -187,8 +187,8 @@ const char* config_weight_list::print(char *buf, int buf_len) } -server_addr::server_addr(const char *hostname, int port) : - m_hostname(hostname), m_port(port), m_server_addr(NULL), m_used_addr(NULL), m_last_error(0) +server_addr::server_addr(const char *hostname, int port, transport_protocol protocol) : + m_hostname(hostname), m_port(port), m_protocol(protocol), m_server_addr(NULL), m_used_addr(NULL), m_last_error(0) { int error = resolve(); @@ -215,8 +215,7 @@ int server_addr::resolve(void) memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; - //hints.ai_socktype = SOCK_STREAM; // FIXME UDP adaptation - hints.ai_socktype = SOCK_DGRAM; + hints.ai_socktype = m_protocol; hints.ai_family = AF_INET; // Don't play with IPv6 for now... snprintf(port_str, sizeof(port_str)-1, "%u", m_port); diff --git a/config_types.h b/config_types.h index 318549aa..d642386c 100644 --- a/config_types.h +++ b/config_types.h @@ -77,7 +77,8 @@ struct connect_info { }; struct server_addr { - server_addr(const char *hostname, int port); + enum transport_protocol {TCP=SOCK_STREAM, UDP=SOCK_DGRAM}; + server_addr(const char *hostname, int port, transport_protocol proto); virtual ~server_addr(); int get_connect_info(struct connect_info *ci); @@ -88,6 +89,7 @@ struct server_addr { std::string m_hostname; int m_port; + int m_protocol; struct addrinfo *m_server_addr; struct addrinfo *m_used_addr; int m_last_error; diff --git a/libmemcached_protocol/binary.h b/libmemcached_protocol/binary.h index 0bb65fa3..eb891c09 100644 --- a/libmemcached_protocol/binary.h +++ b/libmemcached_protocol/binary.h @@ -179,13 +179,28 @@ extern "C" PROTOCOL_BINARY_RAW_BYTES = 0x00 } protocol_binary_datatypes; + /** + * Definition of the extra header that is used when using the + * memcached binary protocol over UDP. This header lies between + * the UDP header and the memcached datagram. + * See https://github.com/memcached/memcached/blob/master/doc/protocol.txt#L922 + */ + typedef union { + struct { + uint16_t request_id; + uint16_t sequence_no; + uint16_t total_datagrams; + uint16_t reserved; // Must be set to 0 + } header; + uint8_t bytes[8]; + } protocol_binary_udp_header; + /** * Definition of the header structure for a request packet. * See section 2 */ typedef union { struct { - uint64_t udp_header; // FIXME UDP adaptation uint8_t magic; uint8_t opcode; uint16_t keylen; @@ -205,7 +220,6 @@ extern "C" */ typedef union { struct { - uint64_t udp_header; // FIXME UDP adaptation uint8_t magic; uint8_t opcode; uint16_t keylen; diff --git a/memtier_benchmark.cpp b/memtier_benchmark.cpp index acff8d3f..62e3cf95 100755 --- a/memtier_benchmark.cpp +++ b/memtier_benchmark.cpp @@ -121,7 +121,8 @@ static void config_print(FILE *file, struct benchmark_config *cfg) "select-db = %d\n" "no-expiry = %s\n" "transaction_latency = %s\n" - "key-width = %u\n", + "key-width = %u\n" + "udp = %s\n", cfg->server, cfg->port, cfg->unix_socket, @@ -159,7 +160,8 @@ static void config_print(FILE *file, struct benchmark_config *cfg) cfg->select_db, cfg->no_expiry ? "yes" : "no", cfg->transaction_latency ? "yes" : "no", - cfg->key_width); + cfg->key_width, + cfg->use_udp ? "yes" : "no"); } static void config_init_defaults(struct benchmark_config *cfg) @@ -250,6 +252,7 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf o_no_expiry, o_transaction_latency, o_key_width, + o_use_udp, }; static struct option long_options[] = { @@ -297,6 +300,7 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf { "version", 0, 0, 'v' }, { "transaction_latency", 0, 0, o_transaction_latency }, { "key-width", 1, 0, o_key_width }, + { "udp", 0, 0, o_use_udp }, { NULL, 0, 0, 0 } }; @@ -574,7 +578,10 @@ static int config_parse_args(int argc, char *argv[], struct benchmark_config *cf return -1; } break; - + case o_use_udp: + //FIXME check that key and value size can fit in a datagram + fprintf(stderr, "Warning: generating traffic over UDP is still experimental. Check that the size of requests can fit in a UDP datagram."); + cfg->use_udp = true; break; default: return -1; @@ -593,6 +600,7 @@ void usage() { " -s, --server=ADDR Server address (default: localhost)\n" " -p, --port=PORT Server port (default: 6379)\n" " -S, --unix-socket=SOCKET UNIX Domain socket name (default: none)\n" + " --udp Connect using UDP rather than TCP (default: false)\n" " -P, --protocol=PROTOCOL Protocol to use (default: redis). Other\n" " supported protocols are memcache_text,\n" " memcache_binary.\n" @@ -676,7 +684,7 @@ struct cg_thread { cg_thread(unsigned int id, benchmark_config* config, object_generator* obj_gen) : m_thread_id(id), m_config(config), m_obj_gen(obj_gen), m_cg(NULL), m_protocol(NULL), m_finished(false) { - m_protocol = protocol_factory(m_config->protocol); + m_protocol = protocol_factory(m_config->protocol, m_config->use_udp); assert(m_protocol != NULL); m_cg = new client_group(m_config, m_protocol, m_obj_gen); @@ -893,7 +901,7 @@ int main(int argc, char *argv[]) if (cfg.server != NULL && cfg.port > 0) { try { - cfg.server_addr = new server_addr(cfg.server, cfg.port); + cfg.server_addr = new server_addr(cfg.server, cfg.port, cfg.use_udp ? server_addr::UDP : server_addr::TCP); } catch (std::runtime_error& e) { benchmark_error_log("%s:%u: error: %s\n", cfg.server, cfg.port, e.what()); @@ -1119,7 +1127,7 @@ int main(int argc, char *argv[]) // If needed, data verification is done now... if (cfg.data_verify) { struct event_base *verify_event_base = event_base_new(); - abstract_protocol *verify_protocol = protocol_factory(cfg.protocol); + abstract_protocol *verify_protocol = protocol_factory(cfg.protocol, cfg.use_udp); verify_client *client = new verify_client(verify_event_base, &cfg, verify_protocol, obj_gen); fprintf(outfile, "\n\nPerforming data verification...\n"); diff --git a/memtier_benchmark.h b/memtier_benchmark.h index 3e84ce83..46201b9f 100644 --- a/memtier_benchmark.h +++ b/memtier_benchmark.h @@ -77,6 +77,7 @@ struct benchmark_config { bool resolve_on_connect; bool transaction_latency; unsigned short key_width; + bool use_udp; }; diff --git a/protocol.cpp b/protocol.cpp index 78ac00ca..983aae44 100644 --- a/protocol.cpp +++ b/protocol.cpp @@ -565,11 +565,12 @@ class memcache_binary_protocol : public abstract_protocol { response_state m_response_state; protocol_binary_response_no_extras m_response_hdr; size_t m_response_len; + bool m_over_udp; const char* status_text(void); public: - memcache_binary_protocol() : m_response_state(rs_initial), m_response_len(0) { } - virtual memcache_binary_protocol* clone(void) { return new memcache_binary_protocol(); } + memcache_binary_protocol(bool over_udp) : m_response_state(rs_initial), m_response_len(0), m_over_udp(over_udp) { } + virtual memcache_binary_protocol* clone(void) { return new memcache_binary_protocol(m_over_udp); } virtual int select_db(int db); virtual int authenticate(const char *credentials); virtual int write_command_set(const char *key, int key_len, const char *value, int value_len, int expiry, unsigned int offset); @@ -620,6 +621,18 @@ int memcache_binary_protocol::authenticate(const char *credentials) return sizeof(req) + user_len + passwd_len + 2 + sizeof(mechanism) - 1; } +// FIXME this currently produces a constant value. +int append_binary_udp_header(struct evbuffer* write_buf) { + protocol_binary_udp_header binary_udp_head; + memset(&binary_udp_head, 0, sizeof(binary_udp_head)); + binary_udp_head.header.request_id = 0; + binary_udp_head.header.sequence_no = 0; + binary_udp_head.header.total_datagrams = 0x0100; //16-bit "0x01" in big endian. + binary_udp_head.header.reserved = 0; + evbuffer_add(write_buf, &binary_udp_head, sizeof(binary_udp_head)); + return sizeof(binary_udp_head); +} + int memcache_binary_protocol::write_command_set(const char *key, int key_len, const char *value, int value_len, int expiry, unsigned int offset) { assert(key != NULL); @@ -630,7 +643,6 @@ int memcache_binary_protocol::write_command_set(const char *key, int key_len, co protocol_binary_request_set req; memset(&req, 0, sizeof(req)); - req.message.header.request.udp_header = 0x0000010000000000; //FIXME fudge req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_SET; req.message.header.request.keylen = htons(key_len); @@ -639,11 +651,16 @@ int memcache_binary_protocol::write_command_set(const char *key, int key_len, co req.message.header.request.extlen = sizeof(req.message.body); req.message.body.expiration = htonl(expiry); + int binary_udp_head_size = 0; + if (m_over_udp) { + binary_udp_head_size = append_binary_udp_header(m_write_buf); + } + evbuffer_add(m_write_buf, &req, sizeof(req)); evbuffer_add(m_write_buf, key, key_len); evbuffer_add(m_write_buf, value, value_len); - return sizeof(req) + key_len + value_len; + return binary_udp_head_size + sizeof(req) + key_len + value_len; } int memcache_binary_protocol::write_command_get(const char *key, int key_len, unsigned int offset) @@ -654,7 +671,6 @@ int memcache_binary_protocol::write_command_get(const char *key, int key_len, un protocol_binary_request_get req; memset(&req, 0, sizeof(req)); - req.message.header.request.udp_header = 0x0000010000000000; //FIXME fudge req.message.header.request.magic = PROTOCOL_BINARY_REQ; req.message.header.request.opcode = PROTOCOL_BINARY_CMD_GET; req.message.header.request.keylen = htons(key_len); @@ -662,10 +678,15 @@ int memcache_binary_protocol::write_command_get(const char *key, int key_len, un req.message.header.request.bodylen = htonl(key_len); req.message.header.request.extlen = 0; + int binary_udp_head_size = 0; + if (m_over_udp) { + binary_udp_head_size = append_binary_udp_header(m_write_buf); + } + evbuffer_add(m_write_buf, &req, sizeof(req)); evbuffer_add(m_write_buf, key, key_len); - return sizeof(req) + key_len; + return binary_udp_head_size + sizeof(req) + key_len; } int memcache_binary_protocol::write_command_multi_get(const keylist *keylist) @@ -724,6 +745,13 @@ int memcache_binary_protocol::parse_response(void) if (evbuffer_get_length(m_read_buf) < sizeof(m_response_hdr)) return 0; // no header yet? + if (m_over_udp) { + protocol_binary_udp_header header; + // FIXME we currently do not check the returned header. + ret = evbuffer_remove(m_read_buf, (void *)&header, sizeof(header)); + assert(ret == sizeof(header)); + } + ret = evbuffer_remove(m_read_buf, (void *)&m_response_hdr, sizeof(m_response_hdr)); assert(ret == sizeof(m_response_hdr)); @@ -802,7 +830,7 @@ int memcache_binary_protocol::parse_response(void) ///////////////////////////////////////////////////////////////////////// -class abstract_protocol *protocol_factory(const char *proto_name) +class abstract_protocol *protocol_factory(const char *proto_name, bool over_udp) { assert(proto_name != NULL); @@ -811,7 +839,7 @@ class abstract_protocol *protocol_factory(const char *proto_name) } else if (strcmp(proto_name, "memcache_text") == 0) { return new memcache_text_protocol(); } else if (strcmp(proto_name, "memcache_binary") == 0) { - return new memcache_binary_protocol(); + return new memcache_binary_protocol(over_udp); } else { benchmark_error_log("Error: unknown protocol '%s'.\n", proto_name); return NULL; diff --git a/protocol.h b/protocol.h index 2de923f5..9d3c6c45 100644 --- a/protocol.h +++ b/protocol.h @@ -102,6 +102,6 @@ class abstract_protocol { struct protocol_response* get_response(void) { return &m_last_response; } }; -class abstract_protocol *protocol_factory(const char *proto_name); +class abstract_protocol *protocol_factory(const char *proto_name, bool over_udp); #endif /* _PROTOCOL_H */