]>
git.proxmox.com Git - ceph.git/blob - ceph/src/test/kv_store_bench.cc
4 * Created on: Aug 23, 2012
8 #include "test/kv_store_bench.h"
9 #include "key_value_store/key_value_structure.h"
10 #include "key_value_store/kv_flat_btree_async.h"
11 #include "include/rados/librados.hpp"
12 #include "test/omap_bench.h"
13 #include "common/ceph_argparse.h"
22 KvStoreBench::KvStoreBench()
47 KvStoreBench::~KvStoreBench()
50 librados::ObjectWriteOperation owo
;
52 io_ctx
.operate(client_name
+ ".done-setting", &owo
);
57 int KvStoreBench::setup(int argc
, const char** argv
) {
58 auto args
= argv_to_vec(argc
, argv
);
63 << "Usage: KvStoreBench [options]\n"
64 << "Generate latency and throughput statistics for the key value store\n"
66 << "There are two sets of options - workload options affect the kind of\n"
67 << "test to run, while algorithm options affect how the key value\n"
68 << "store handles the workload.\n"
70 << "There are about entries / k objects in the store to begin with.\n"
71 << "Higher k values reduce the likelihood of splits and the likelihood\n"
72 << "multiple writers simultaneously faling to write because an object \n"
73 << "is full, but having a high k also means there will be more object\n"
76 << "WORKLOAD OPTIONS\n"
77 << " --name <client name> client name (default admin)\n"
78 << " --entries <number> number of key/value pairs to store initially\n"
79 << " (default " << entries
<< ")\n"
80 << " --ops <number> number of operations to run\n"
81 << " --keysize <number> number of characters per key (default " << key_size
<< ")\n"
82 << " --valsize <number> number of characters per value (default " << val_size
<< ")\n"
83 << " -t <number> number of operations in flight concurrently\n"
84 << " (default " << max_ops_in_flight
<< ")\n"
85 << " --clients <number> tells this instance how many total clients are. Note that\n"
86 << " changing this does not change the number of clients."
87 << " -d <insert> <update> <delete> <read> percent (1-100) of operations that should be of each type\n"
88 << " (default 25 25 25 25)\n"
89 << " -r <number> random seed to use (default time(0))\n"
90 << "ALGORITHM OPTIONS\n"
91 << " --kval k, where each object has a number of entries\n"
92 << " >= k and <= 2k.\n"
93 << " --cache-size number of index entries to keep in cache\n"
94 << " (default " << cache_size
<< ")\n"
95 << " --cache-refresh percent (1-100) of cache-size to read each \n"
96 << " time the index is read\n"
98 << " --verbosity-on display debug output\n"
99 << " --clear-first delete all existing objects in the pool before running tests\n";
100 for (unsigned i
= 0; i
< args
.size(); i
++) {
101 if(i
< args
.size() - 1) {
102 if (strcmp(args
[i
], "--ops") == 0) {
103 ops
= atoi(args
[i
+1]);
104 } else if (strcmp(args
[i
], "--entries") == 0) {
105 entries
= atoi(args
[i
+1]);
106 } else if (strcmp(args
[i
], "--kval") == 0) {
108 } else if (strcmp(args
[i
], "--keysize") == 0) {
109 key_size
= atoi(args
[i
+1]);
110 } else if (strcmp(args
[i
], "--valsize") == 0) {
111 val_size
= atoi(args
[i
+1]);
112 } else if (strcmp(args
[i
], "--cache-size") == 0) {
113 cache_size
= atoi(args
[i
+1]);
114 } else if (strcmp(args
[i
], "--cache-refresh") == 0) {
115 auto temp
= atoi(args
[i
+1]);
117 cache_refresh
= 100 / (double)temp
;
118 } else if (strcmp(args
[i
], "-t") == 0) {
119 max_ops_in_flight
= atoi(args
[i
+1]);
120 } else if (strcmp(args
[i
], "--clients") == 0) {
121 clients
= atoi(args
[i
+1]);
122 } else if (strcmp(args
[i
], "-d") == 0) {
123 if (i
+ 4 >= args
.size()) {
124 cout
<< "Invalid arguments after -d: there must be 4 of them."
129 int sum
= atoi(args
[i
+ 1]);
131 sum
+= atoi(args
[i
+ 2]);
133 sum
+= atoi(args
[i
+ 3]);
135 sum
+= atoi(args
[i
+ 4]);
138 cout
<< "Invalid arguments after -d: they must add to 100."
142 } else if (strcmp(args
[i
], "--name") == 0) {
143 client_name
= args
[i
+1];
144 } else if (strcmp(args
[i
], "-r") == 0) {
145 srand(atoi(args
[i
+1]));
147 } else if (strcmp(args
[i
], "--verbosity-on") == 0) {
149 } else if (strcmp(args
[i
], "--clear-first") == 0) {
151 } else if (strcmp(args
[i
], "--help") == 0) {
152 cout
<< help
.str() << std::endl
;
157 KvFlatBtreeAsync
* kvba
= new KvFlatBtreeAsync(k
, client_name
, cache_size
,
158 cache_refresh
, verbose
);
161 int r
= rados
.init(rados_id
.c_str());
163 cout
<< "error during init" << std::endl
;
166 r
= rados
.conf_parse_argv(argc
, argv
);
168 cout
<< "error during parsing args" << std::endl
;
171 r
= rados
.conf_parse_env(NULL
);
173 cout
<< "error during parsing env" << std::endl
;
176 r
= rados
.conf_read_file(NULL
);
178 cout
<< "error during read file" << std::endl
;
183 cout
<< "error during connect: " << r
<< std::endl
;
186 r
= rados
.ioctx_create(pool_name
.c_str(), io_ctx
);
188 cout
<< "error creating io ctx" << std::endl
;
195 librados::NObjectIterator it
;
196 for (it
= io_ctx
.nobjects_begin(); it
!= io_ctx
.nobjects_end(); ++it
) {
197 librados::ObjectWriteOperation rm
;
199 io_ctx
.operate(it
->get_oid(), &rm
);
203 int err
= kvs
->setup(argc
, argv
);
204 if (err
< 0 && err
!= -17) {
205 cout
<< "error during setup of kvs: " << err
<< std::endl
;
212 string
KvStoreBench::random_string(int len
) {
214 string alphanum
= "0123456789"
215 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
216 "abcdefghijklmnopqrstuvwxyz";
217 for (int i
= 0; i
< len
; ++i
) {
218 ret
.push_back(alphanum
[rand() % (alphanum
.size() - 1)]);
224 pair
<string
, bufferlist
> KvStoreBench::rand_distr(bool new_elem
) {
225 pair
<string
, bufferlist
> ret
;
227 ret
= make_pair(random_string(key_size
),
228 KvFlatBtreeAsync::to_bl(random_string(val_size
)));
229 key_set
.insert(ret
.first
);
231 if (key_set
.size() == 0) {
232 return make_pair("",KvFlatBtreeAsync::to_bl(""));
234 string get_string
= random_string(key_size
);
235 std::set
<string
>::iterator it
= key_set
.lower_bound(get_string
);
236 if (it
== key_set
.end()) {
237 ret
.first
= *(key_set
.rbegin());
241 ret
.second
= KvFlatBtreeAsync::to_bl(random_string(val_size
));
246 int KvStoreBench::test_random_insertions() {
251 stringstream prev_ss
;
252 prev_ss
<< (atoi(client_name
.c_str()) - 1);
253 string prev_rid
= prev_ss
.str();
254 stringstream last_ss
;
255 if (client_name
.size() > 1) {
256 last_ss
<< client_name
.substr(0,client_name
.size() - 2);
258 last_ss
<< clients
- 1;
259 string last_rid
= client_name
== "admin" ? "admin" : last_ss
.str();
261 map
<string
, bufferlist
> big_map
;
262 for (int i
= 0; i
< entries
; i
++) {
264 bfr
.append(random_string(7));
265 big_map
[random_string(5)] = bfr
;
270 if (client_name
[client_name
.size() - 1] != '0' && client_name
!= "admin") {
272 librados::ObjectReadOperation oro
;
273 oro
.stat(&uint
, &t
, &err
);
274 err
= io_ctx
.operate(prev_rid
+ ".done-setting", &oro
, NULL
);
275 if (verbose
) cout
<< "reading " << prev_rid
<< ": err = " << err
278 cout
<< "detected " << prev_rid
<< ".done-setting" << std::endl
;
281 cout
<< "testing random insertions";
282 err
= kvs
->set_many(big_map
);
284 cout
<< "error setting things" << std::endl
;
288 librados::ObjectWriteOperation owo
;
290 io_ctx
.operate(client_name
+ ".done-setting", &owo
);
291 cout
<< "created " << client_name
+ ".done-setting. waiting for "
292 << last_rid
<< ".done-setting" << std::endl
;
295 librados::ObjectReadOperation oro
;
296 oro
.stat(&uint
, &t
, &err
);
297 err
= io_ctx
.operate(last_rid
+ ".done-setting", &oro
, NULL
);
299 cout
<< "detected " << last_rid
<< ".done-setting" << std::endl
;
304 void KvStoreBench::aio_callback_timed(int * err
, void *arg
) {
305 timed_args
*args
= reinterpret_cast<timed_args
*>(arg
);
306 ceph::mutex
* ops_in_flight_lock
= &args
->kvsb
->ops_in_flight_lock
;
307 ceph::mutex
* data_lock
= &args
->kvsb
->data_lock
;
308 ceph::condition_variable
* op_avail
= &args
->kvsb
->op_avail
;
309 int *ops_in_flight
= &args
->kvsb
->ops_in_flight
;
310 if (*err
< 0 && *err
!= -61) {
311 cerr
<< "Error during " << args
->op
<< " operation: " << *err
<< std::endl
;
314 args
->sw
.stop_time();
315 double time
= args
->sw
.get_time();
320 args
->kvsb
->data
.latency_jf
.open_object_section("latency");
321 args
->kvsb
->data
.latency_jf
.dump_float(string(1, args
->op
).c_str(),
323 args
->kvsb
->data
.latency_jf
.close_section();
326 args
->kvsb
->data
.throughput_jf
.open_object_section("throughput");
327 args
->kvsb
->data
.throughput_jf
.dump_unsigned(string(1, args
->op
).c_str(),
329 args
->kvsb
->data
.throughput_jf
.close_section();
333 ops_in_flight_lock
->lock();
335 op_avail
->notify_all();
336 ops_in_flight_lock
->unlock();
341 int KvStoreBench::test_teuthology_aio(next_gen_t distr
,
342 const map
<int, char> &probs
)
345 cout
<< "inserting initial entries..." << std::endl
;
346 err
= test_random_insertions();
350 cout
<< "finished inserting initial entries. Waiting 10 seconds for everyone"
351 << " to catch up..." << std::endl
;
355 cout
<< "done waiting. Starting random operations..." << std::endl
;
357 std::unique_lock l
{ops_in_flight_lock
};
358 for (int i
= 0; i
< ops
; i
++) {
359 ceph_assert(ops_in_flight
<= max_ops_in_flight
);
360 if (ops_in_flight
== max_ops_in_flight
) {
362 ceph_assert(ops_in_flight
< max_ops_in_flight
);
364 cout
<< "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" << i
+ 1 << " / "
366 timed_args
* cb_args
= new timed_args(this);
367 pair
<string
, bufferlist
> kv
;
368 int random
= (rand() % 100);
369 cb_args
->op
= probs
.lower_bound(random
)->second
;
370 switch (cb_args
->op
) {
372 kv
= (((KvStoreBench
*)this)->*distr
)(true);
373 if (kv
.first
== "") {
379 cb_args
->sw
.start_time();
380 kvs
->aio_set(kv
.first
, kv
.second
, false, aio_callback_timed
,
381 cb_args
, &cb_args
->err
);
384 kv
= (((KvStoreBench
*)this)->*distr
)(false);
385 if (kv
.first
== "") {
391 cb_args
->sw
.start_time();
392 kvs
->aio_set(kv
.first
, kv
.second
, true, aio_callback_timed
,
393 cb_args
, &cb_args
->err
);
396 kv
= (((KvStoreBench
*)this)->*distr
)(false);
397 if (kv
.first
== "") {
402 key_set
.erase(kv
.first
);
404 cb_args
->sw
.start_time();
405 kvs
->aio_remove(kv
.first
, aio_callback_timed
, cb_args
, &cb_args
->err
);
408 kv
= (((KvStoreBench
*)this)->*distr
)(false);
409 if (kv
.first
== "") {
415 cb_args
->sw
.start_time();
416 kvs
->aio_get(kv
.first
, &cb_args
->val
, aio_callback_timed
,
417 cb_args
, &cb_args
->err
);
420 // shouldn't happen here
426 op_avail
.wait(l
, [this] { return ops_in_flight
<= 0; });
432 int KvStoreBench::test_teuthology_sync(next_gen_t distr
,
433 const map
<int, char> &probs
)
436 err
= test_random_insertions();
441 for (int i
= 0; i
< ops
; i
++) {
443 pair
<char, double> d
;
444 cout
<< "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" << i
+ 1 << " / "
446 pair
<string
, bufferlist
> kv
;
447 int random
= (rand() % 100);
448 d
.first
= probs
.lower_bound(random
)->second
;
451 kv
= (((KvStoreBench
*)this)->*distr
)(true);
452 if (kv
.first
== "") {
457 err
= kvs
->set(kv
.first
, kv
.second
, true);
460 cout
<< "Error setting " << kv
<< ": " << err
<< std::endl
;
465 kv
= (((KvStoreBench
*)this)->*distr
)(false);
466 if (kv
.first
== "") {
471 err
= kvs
->set(kv
.first
, kv
.second
, true);
473 if (err
< 0 && err
!= -61) {
474 cout
<< "Error updating " << kv
<< ": " << err
<< std::endl
;
479 kv
= (((KvStoreBench
*)this)->*distr
)(false);
480 if (kv
.first
== "") {
484 key_set
.erase(kv
.first
);
486 err
= kvs
->remove(kv
.first
);
488 if (err
< 0 && err
!= -61) {
489 cout
<< "Error removing " << kv
<< ": " << err
<< std::endl
;
494 kv
= (((KvStoreBench
*)this)->*distr
)(false);
495 if (kv
.first
== "") {
501 err
= kvs
->get(kv
.first
, &kv
.second
);
503 if (err
< 0 && err
!= -61) {
504 cout
<< "Error getting " << kv
<< ": " << err
<< std::endl
;
510 double time
= sw
.get_time();
514 data
.latency_jf
.open_object_section("latency");
515 data
.latency_jf
.dump_float(string(1, d
.first
).c_str(),
517 data
.latency_jf
.close_section();
524 void KvStoreBench::print_time_data() {
525 cout
<< "========================================================\n";
526 cout
<< "latency:" << std::endl
;
527 data
.latency_jf
.flush(cout
);
529 cout
<< "throughput:" << std::endl
;
530 data
.throughput_jf
.flush(cout
);
531 cout
<< "\n========================================================"
535 int KvStoreBench::teuthology_tests() {
537 if (max_ops_in_flight
> 1) {
538 err
= test_teuthology_aio(&KvStoreBench::rand_distr
, probs
);
540 err
= test_teuthology_sync(&KvStoreBench::rand_distr
, probs
);
545 int main(int argc
, const char** argv
) {
547 int err
= kvsb
.setup(argc
, argv
);
548 if (err
== 0) cout
<< "setup successful" << std::endl
;
550 cout
<< "error " << err
<< std::endl
;
553 err
= kvsb
.teuthology_tests();
554 if (err
< 0) return err
;