memcache_test.c
changeset 49 10c7dce1a043
parent 48 1c67f512779b
equal deleted inserted replaced
48:1c67f512779b 49:10c7dce1a043
    37 #define MIN(a, b) ((a) < (b) ? (a) : (b))
    37 #define MIN(a, b) ((a) < (b) ? (a) : (b))
    38 #define MAX(a, b) ((a) > (b) ? (a) : (b))
    38 #define MAX(a, b) ((a) > (b) ? (a) : (b))
    39 
    39 
    40 struct common {
    40 struct common {
    41     unsigned int max_connections;
    41     unsigned int max_connections;
       
    42     char pipeline_requests;
    42     
    43     
    43     struct timeval start;
    44     struct timeval start;
    44 } common;
    45 } common;
    45 
    46 
    46 struct benchmark_fetch {
    47 struct benchmark_fetch {
    63 } benchmark_fetch;
    64 } benchmark_fetch;
    64 
    65 
    65 void benchmark_cb (struct memcache_req *req, void *arg);
    66 void benchmark_cb (struct memcache_req *req, void *arg);
    66 void benchmark_fetch_fn (void);
    67 void benchmark_fetch_fn (void);
    67 
    68 
       
    69 enum option_test {
       
    70     TEST_INVALID,
       
    71     TEST_COMMON,
       
    72 
       
    73     TEST_BENCH_FETCH,
       
    74 };
       
    75 
    68 enum option_code {
    76 enum option_code {
    69     OPT_CODE_INVALID,
    77     OPT_CODE_INVALID,
    70 
    78 
    71     COMMON_CONN_MAX,
    79     COMMON_CONN_MAX,
       
    80     COMMON_PIPELINE,
    72     BENCH_FETCH_REQ_CONCURRENCY,
    81     BENCH_FETCH_REQ_CONCURRENCY,
    73     BENCH_FETCH_REQ_AMOUNT,
    82     BENCH_FETCH_REQ_AMOUNT,
    74     BENCH_FETCH_KEY_PREFIX,
    83     BENCH_FETCH_KEY_PREFIX,
    75     BENCH_FETCH_KEY_LEN_MIN,
    84     BENCH_FETCH_KEY_LEN_MIN,
    76     BENCH_FETCH_KEY_LEN_MAX,
    85     BENCH_FETCH_KEY_LEN_MAX,
    81     OPT_CODE_MAX,
    90     OPT_CODE_MAX,
    82 };
    91 };
    83 
    92 
    84 enum option_type {
    93 enum option_type {
    85     OPT_TYPE_NONE,
    94     OPT_TYPE_NONE,
       
    95     OPT_TYPE_BOOL,
    86     OPT_TYPE_UINT,
    96     OPT_TYPE_UINT,
    87     OPT_TYPE_STR,
    97     OPT_TYPE_STR,
    88 };
    98 };
    89 
    99 
    90 static struct test {
   100 static struct test {
    91     char *name;
   101     char *name;
    92     memcache_cb cb_fn;
   102     memcache_cb cb_fn;
    93 
   103 
    94     void (*test_fn) (void);
   104     void (*test_fn) (void);
    95 
   105 
       
   106     enum option_test code;
       
   107 
       
   108     char *descr;
       
   109 
    96 } test_list[] = {
   110 } test_list[] = {
    97     { "benchmark_fetch",        &benchmark_cb,  &benchmark_fetch_fn },
   111     { "benchmark_fetch",        &benchmark_cb,  &benchmark_fetch_fn,    TEST_BENCH_FETCH,
    98     { 0,                        0,              0,                  }
   112         "Measure the speed of fetch requests"                                               },
       
   113     { 0,                        0,              0,                      0,  0               }
    99 };
   114 };
   100 
   115 
   101 static struct option options[] = {
   116 static struct option options[] = {
   102     { "conn-max",           required_argument,  NULL,   COMMON_CONN_MAX             },
   117     { "conn-max",           required_argument,  NULL,   COMMON_CONN_MAX             },
       
   118     { "pipeline",           required_argument,  NULL,   COMMON_PIPELINE             },
   103     { "req-concurrency",    required_argument,  NULL,   BENCH_FETCH_REQ_CONCURRENCY },
   119     { "req-concurrency",    required_argument,  NULL,   BENCH_FETCH_REQ_CONCURRENCY },
   104     { "req-amount",         required_argument,  NULL,   BENCH_FETCH_REQ_AMOUNT      },
   120     { "req-amount",         required_argument,  NULL,   BENCH_FETCH_REQ_AMOUNT      },
   105     { "key-prefix",         required_argument,  NULL,   BENCH_FETCH_KEY_PREFIX      },
   121     { "key-prefix",         required_argument,  NULL,   BENCH_FETCH_KEY_PREFIX      },
   106     { "key-len-min",        required_argument,  NULL,   BENCH_FETCH_KEY_LEN_MIN     },
   122     { "key-len-min",        required_argument,  NULL,   BENCH_FETCH_KEY_LEN_MIN     },
   107     { "key-len-max",        required_argument,  NULL,   BENCH_FETCH_KEY_LEN_MAX     },
   123     { "key-len-max",        required_argument,  NULL,   BENCH_FETCH_KEY_LEN_MAX     },
   110     { "data-len-max",       required_argument,  NULL,   BENCH_FETCH_DATA_LEN_MAX    },
   126     { "data-len-max",       required_argument,  NULL,   BENCH_FETCH_DATA_LEN_MAX    },
   111     { 0,                    0,                  0,      0                           },
   127     { 0,                    0,                  0,      0                           },
   112 };
   128 };
   113 
   129 
   114 static struct opt {
   130 static struct opt {
       
   131     enum option_test test;
   115     enum option_code code;
   132     enum option_code code;
   116     enum option_type type;
   133     enum option_type type;
   117     
   134     
   118     union opt_type_data {
   135     union opt_type_data {
       
   136         struct  {
       
   137             char *value;
       
   138             char default_value;
       
   139         } bool;
       
   140 
   119         struct {
   141         struct {
   120             unsigned int *value;
   142             unsigned int *value;
   121             unsigned int default_value;
   143             unsigned int default_value;
   122         } uint;
   144         } uint;
   123 
   145 
   124         struct {
   146         struct {
   125             const char **value;
   147             const char **value;
   126             const char *default_value;
   148             const char *default_value;
   127         } str;
   149         } str;
   128     } data;
   150     } data;
       
   151     
       
   152     const char *name;
       
   153     const char *descr;
   129 } option_info[OPT_CODE_MAX] = {
   154 } option_info[OPT_CODE_MAX] = {
   130     {   OPT_CODE_INVALID,               OPT_TYPE_NONE                                                           },
   155     {   TEST_INVALID,       OPT_CODE_INVALID,               OPT_TYPE_NONE                                                           },
   131     {   COMMON_CONN_MAX,                OPT_TYPE_UINT,  { .uint = { &common.max_connections,          1     }}  },
   156 
   132     {   BENCH_FETCH_REQ_CONCURRENCY,    OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.concurrent_ops,  1     }}  },
   157     {   TEST_COMMON,        COMMON_CONN_MAX,                OPT_TYPE_UINT,  { .uint = { &common.max_connections,          1     }},
   133     {   BENCH_FETCH_REQ_AMOUNT,         OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.total_ops,       500   }}  },
   158             "conn-max",             "number of connections to use"                                                                  },
   134     {   BENCH_FETCH_KEY_PREFIX,         OPT_TYPE_STR,   { .str  = { &benchmark_fetch.key_prefix,      "bf_" }}  },
   159 
   135     {   BENCH_FETCH_KEY_LEN_MIN,        OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.key_len_min,     8     }}  },
   160     {   TEST_COMMON,        COMMON_PIPELINE,                OPT_TYPE_BOOL,  { .bool = { &common.pipeline_requests,        1     }},  
   136     {   BENCH_FETCH_KEY_LEN_MAX,        OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.key_len_max,     8     }}  },
   161             "pipeline",             "pipeline requests"                                                                             },
   137     {   BENCH_FETCH_KEY_COUNT,          OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.key_count,       1     }}  },
   162 
   138     {   BENCH_FETCH_DATA_LEN_MIN,       OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.data_len_min,    64    }}  },
   163     {   TEST_BENCH_FETCH,   BENCH_FETCH_REQ_CONCURRENCY,    OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.concurrent_ops,  1     }},
   139     {   BENCH_FETCH_DATA_LEN_MAX,       OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.data_len_max,    64    }}  },
   164             "req-concurrency",      "number of requests to have running"                                                            },
       
   165 
       
   166     {   TEST_BENCH_FETCH,   BENCH_FETCH_REQ_AMOUNT,         OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.total_ops,       500   }},
       
   167             "req-amount",           "number of requests to issue"                                                                   },
       
   168 
       
   169     {   TEST_BENCH_FETCH,   BENCH_FETCH_KEY_PREFIX,         OPT_TYPE_STR,   { .str  = { &benchmark_fetch.key_prefix,      "bf_" }},
       
   170             "key-prefix",           "key prefix"                                                                                    },
       
   171 
       
   172     {   TEST_BENCH_FETCH,   BENCH_FETCH_KEY_LEN_MIN,        OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.key_len_min,     8     }},  
       
   173             "key-len-min",          "minimum key length"                                                                            },
       
   174 
       
   175     {   TEST_BENCH_FETCH,   BENCH_FETCH_KEY_LEN_MAX,        OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.key_len_max,     8     }},
       
   176             "key-len-max",          "maximum key length"                                                                            },
       
   177 
       
   178     {   TEST_BENCH_FETCH,   BENCH_FETCH_KEY_COUNT,          OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.key_count,       1     }},
       
   179             "key-count",            "how many keys to use"                                                                          },
       
   180 
       
   181     {   TEST_BENCH_FETCH,   BENCH_FETCH_DATA_LEN_MIN,       OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.data_len_min,    64    }},
       
   182             "data-len-min",         "minimum data length"                                                                           },
       
   183 
       
   184     {   TEST_BENCH_FETCH,   BENCH_FETCH_DATA_LEN_MAX,       OPT_TYPE_UINT,  { .uint = { &benchmark_fetch.data_len_max,    64    }},
       
   185             "data-len-max",         "maximum data length"                                                                            },
   140 };
   186 };
   141 
   187 
   142 void time_reset () {
   188 void time_reset () {
   143     // start timing
   189     // start timing
   144     assert(gettimeofday(&common.start, NULL) == 0);
   190     assert(gettimeofday(&common.start, NULL) == 0);
   150     assert(gettimeofday(&time, NULL) == 0);
   196     assert(gettimeofday(&time, NULL) == 0);
   151     
   197     
   152     return ((double) (time.tv_sec - common.start.tv_sec)) + ((double) (time.tv_usec - common.start.tv_usec)) / 1000000;
   198     return ((double) (time.tv_sec - common.start.tv_sec)) + ((double) (time.tv_usec - common.start.tv_usec)) / 1000000;
   153 }
   199 }
   154 
   200 
       
   201 int ratelimit (int count, int total) {
       
   202     return ((total / 10) ? (count % (total / 10) == 0) : 1);
       
   203 }
       
   204 
   155 
   205 
   156 
   206 
   157 void mc_init (int max_connections, memcache_cb cb_fn) {
   207 void mc_init (int max_connections, memcache_cb cb_fn) {
   158     // memcache init    
   208     // memcache init    
   159     if ((mc = memcache_alloc(cb_fn)) == NULL)
   209     if ((mc = memcache_alloc(cb_fn, common.pipeline_requests)) == NULL)
   160         ERROR("memcache_alloc");
   210         ERROR("memcache_alloc");
   161     
   211     
   162     // fix up the endpoint
   212     // fix up the endpoint
   163     endpoint_init(&server_endpoint, 11211);
   213     endpoint_init(&server_endpoint, 11211);
   164     
   214     
   165     if (endpoint_parse(&server_endpoint, "localhost"))
   215     if (endpoint_parse(&server_endpoint, "localhost"))
   166         ERROR("config_endpoint_parse");
   216         ERROR("config_endpoint_parse");
   167     
   217     
   168     // add the server
   218     // add the server
   169     if (memcache_add_server(mc, &server_endpoint, max_connections))
   219     if (memcache_add_server(mc, &server_endpoint, common.max_connections))
   170         ERROR("memcache_add_server");
   220         ERROR("memcache_add_server");
   171     
   221     
   172     INFO("[memcache] initialized with max_connections=%d", max_connections);
   222     INFO("[memcache] initialized with pipeline_requests=%c max_connections=%d", common.pipeline_requests ? 't' : 'f', common.max_connections);
   173 
   223 
   174     return;
   224     return;
   175 
   225 
   176 error:
   226 error:
   177     assert(0);
   227     assert(0);
   179 
   229 
   180 void dump_req (struct memcache_req *req, void *unused) {
   230 void dump_req (struct memcache_req *req, void *unused) {
   181     const struct memcache_obj *obj;
   231     const struct memcache_obj *obj;
   182     const struct memcache_buf *buf;
   232     const struct memcache_buf *buf;
   183 
   233 
   184     INFO("[%*s]: cmd=%s state=%s reply=%s",
   234     INFO("[%.*s]: cmd=%s state=%s reply=%s",
   185         (int) memcache_req_key(req)->len, memcache_req_key(req)->buf,
   235         (int) memcache_req_key(req)->len, memcache_req_key(req)->buf,
   186         memcache_command_str(memcache_req_cmd(req)),
   236         memcache_command_str(memcache_req_cmd(req)),
   187         memcache_state_str(memcache_req_state(req)),
   237         memcache_state_str(memcache_req_state(req)),
   188         memcache_reply_str(memcache_req_reply(req))
   238         memcache_reply_str(memcache_req_reply(req))
   189     );
   239     );
   286     while (benchmark_fetch.cur_ops < benchmark_fetch.concurrent_ops && (benchmark_fetch.op_count + benchmark_fetch.cur_ops) < benchmark_fetch.total_ops) {
   336     while (benchmark_fetch.cur_ops < benchmark_fetch.concurrent_ops && (benchmark_fetch.op_count + benchmark_fetch.cur_ops) < benchmark_fetch.total_ops) {
   287         // launch
   337         // launch
   288         assert(memcache_fetch(mc, &benchmark_fetch.keys[random_value(0, benchmark_fetch.key_count)].key, NULL) != NULL);
   338         assert(memcache_fetch(mc, &benchmark_fetch.keys[random_value(0, benchmark_fetch.key_count)].key, NULL) != NULL);
   289 
   339 
   290         benchmark_fetch.cur_ops++;
   340         benchmark_fetch.cur_ops++;
   291 
   341         
   292         if ((benchmark_fetch.op_count + benchmark_fetch.cur_ops) % (benchmark_fetch.total_ops / 10) == 0)
   342         if (ratelimit(benchmark_fetch.op_count + benchmark_fetch.cur_ops, benchmark_fetch.total_ops))
   293             INFO("[benchmark] %0.6f: %d+%d/%d requests", 
   343             INFO("[benchmark] %0.6f: %d+%d/%d requests", 
   294                 time_offset(),
   344                 time_offset(),
   295                 benchmark_fetch.op_count, benchmark_fetch.cur_ops, benchmark_fetch.total_ops
   345                 benchmark_fetch.op_count, benchmark_fetch.cur_ops, benchmark_fetch.total_ops
   296             );
   346             );
   297     }
   347     }
   355                 dump_req(req, arg);
   405                 dump_req(req, arg);
   356                 WARNING("value was not stored");
   406                 WARNING("value was not stored");
   357             }
   407             }
   358 
   408 
   359             benchmark_fetch.keys_stored++;
   409             benchmark_fetch.keys_stored++;
   360 
   410             
   361             if (benchmark_fetch.keys_stored % (benchmark_fetch.key_count / 10) == 0)
   411             if (ratelimit(benchmark_fetch.keys_stored, benchmark_fetch.key_count))
   362                 INFO("[benchmark] %.6f: key %u/%u stored: %.*s", 
   412                 INFO("[benchmark] %.6f: key %u/%u stored: %.*s", 
   363                     time_offset(), benchmark_fetch.keys_stored, benchmark_fetch.key_count, (int) memcache_req_key(req)->len, memcache_req_key(req)->buf
   413                     time_offset(), benchmark_fetch.keys_stored, benchmark_fetch.key_count, (int) memcache_req_key(req)->len, memcache_req_key(req)->buf
   364                 );
   414                 );
   365 
   415 
   366             if (benchmark_fetch.keys_stored == benchmark_fetch.key_count)
   416             if (benchmark_fetch.keys_stored == benchmark_fetch.key_count)
   417 
   467 
   418         assert(memcache_store(mc, MEMCACHE_CMD_STORE_SET, &keybuf->key, &obj, &buf, NULL) != NULL);
   468         assert(memcache_store(mc, MEMCACHE_CMD_STORE_SET, &keybuf->key, &obj, &buf, NULL) != NULL);
   419     }
   469     }
   420 }
   470 }
   421 
   471 
       
   472 void usage_opt (enum option_test test) {
       
   473     struct opt *opt;
       
   474 
       
   475     for (opt = option_info + 1; opt->code; opt++) {
       
   476         if (opt->test == test) {
       
   477             switch (opt->type) {
       
   478                 case OPT_TYPE_BOOL:
       
   479                     INFO("\t%-20s %c     %s", opt->name, opt->data.bool.default_value ? 't' : 'f', opt->descr);
       
   480                     break;
       
   481 
       
   482                 case OPT_TYPE_UINT:
       
   483                     INFO("\t%-20s %-6d %s", opt->name, opt->data.uint.default_value, opt->descr);
       
   484                     break;
       
   485 
       
   486                 case OPT_TYPE_STR:
       
   487                     INFO("\t%-20s %-6s %s", opt->name, opt->data.str.default_value, opt->descr);
       
   488                     break;
       
   489                 
       
   490                 default:
       
   491                     assert(0);
       
   492             }
       
   493         }
       
   494     }
       
   495 }
       
   496 
   422 void usage (char *cmd) {
   497 void usage (char *cmd) {
   423     INFO(
   498     struct test *test;
   424         "Usage: %s <cmd> [<args> ... ]\n"
   499 
   425         "\n"
   500     INFO("Usage: %s <cmd> [<args> ... ]", cmd);
   426         "COMMANDS\n"
   501 
   427         "\n"
   502     INFO("\nCOMMON OPTIONS\n");
   428         "benchmark_fetch:\n"
   503     
   429         "\tMeasure the speed of fetch requests\n"
   504     usage_opt(TEST_COMMON);
   430         "\t\n"
   505 
   431         "\tconn-max         1       number of connections to use\n"
   506     INFO("\nCOMMANDS\n");
   432         "\treq-concurrency  1       number of requests to have running\n"
   507 
   433         "\treq-amount       500     number of requests to issue\n"
   508     for (test = test_list; test->name; test++) {
   434         "\tkey-prefix       bf_     key prefix\n"
   509         INFO("%s:", test->name);
   435         "\tkey-len-min      8       minimum key lengt\n"
   510         INFO("\t%s", test->descr);
   436         "\tkey-len-max      8       maximum key length\n"
   511         INFO("\t");
   437         "\tkey-count        1       how many keys to use\n"
   512         
   438         "\tdata-len-min     64      minimum data length\n"
   513         usage_opt(test->code);
   439         "\tdata-len-max     64      maximum data length\n",
   514 
   440 
   515         INFO("\t");
   441         cmd
   516     }
   442     );
       
   443 
   517 
   444     exit(1);
   518     exit(1);
   445 }
   519 }
   446 
   520 
   447 int main (int argc, char **argv) {
   521 int main (int argc, char **argv) {
   470     // default values
   544     // default values
   471     for (c = OPT_CODE_INVALID; c < OPT_CODE_MAX; c++) {
   545     for (c = OPT_CODE_INVALID; c < OPT_CODE_MAX; c++) {
   472         switch (option_info[c].type) {
   546         switch (option_info[c].type) {
   473             case OPT_TYPE_NONE:
   547             case OPT_TYPE_NONE:
   474                 break;
   548                 break;
       
   549             
       
   550             case OPT_TYPE_BOOL:
       
   551                 *option_info[c].data.bool.value = option_info[c].data.bool.default_value;
       
   552 
       
   553                 break;
   475 
   554 
   476             case OPT_TYPE_UINT:
   555             case OPT_TYPE_UINT:
   477                 *option_info[c].data.uint.value = option_info[c].data.uint.default_value;
   556                 *option_info[c].data.uint.value = option_info[c].data.uint.default_value;
   478 
   557 
   479                 break;
   558                 break;
   489     }
   568     }
   490 
   569 
   491     while ((c = getopt_long(argc, argv, "", options, &option_index)) != -1) {
   570     while ((c = getopt_long(argc, argv, "", options, &option_index)) != -1) {
   492         if (c <= OPT_CODE_INVALID || c >= OPT_CODE_MAX)
   571         if (c <= OPT_CODE_INVALID || c >= OPT_CODE_MAX)
   493             FATAL("invalid argument %s", options[option_index].name);
   572             FATAL("invalid argument %s", options[option_index].name);
       
   573 
       
   574         if (option_info[c].test != test->code && option_info[c].test != TEST_COMMON)
       
   575             FATAL("invalid option %s for test %s", options[option_index].name, test->name);
   494         
   576         
   495         switch (option_info[c].type) {
   577         switch (option_info[c].type) {
       
   578             case OPT_TYPE_BOOL:
       
   579                 assert(optarg);
       
   580 
       
   581                 switch (optarg[0]) {
       
   582                     case 't':
       
   583                     case '1':
       
   584                         *option_info[c].data.bool.value = 1;
       
   585                         break;
       
   586 
       
   587                     case 'f':
       
   588                     case '0':
       
   589                         *option_info[c].data.bool.value = 0;
       
   590                         break;
       
   591 
       
   592                     default:
       
   593                         FATAL("invalid true/false value: %s: %s", options[option_index].name, optarg);
       
   594                 }
       
   595 
       
   596                 break;
       
   597 
   496             case OPT_TYPE_UINT:
   598             case OPT_TYPE_UINT:
   497                 assert(optarg);
   599                 assert(optarg);
   498 
   600 
   499                 *option_info[c].data.uint.value = strtol(optarg, &invalid, 10);
   601                 *option_info[c].data.uint.value = strtol(optarg, &invalid, 10);
   500 
   602