]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
f67539c2 | 2 | #include <iterator> |
7c673cae FG |
3 | #include <map> |
4 | #include <set> | |
5 | #include <boost/scoped_ptr.hpp> | |
6 | ||
7 | #include "include/buffer.h" | |
8 | #include "test/ObjectMap/KeyValueDBMemory.h" | |
9 | #include "kv/KeyValueDB.h" | |
10 | #include "os/filestore/DBObjectMap.h" | |
11 | #include "os/filestore/HashIndex.h" | |
12 | #include <sys/types.h> | |
13 | #include "global/global_init.h" | |
14 | #include "common/ceph_argparse.h" | |
15 | #include <dirent.h> | |
16 | ||
17 | #include "gtest/gtest.h" | |
18 | #include "stdlib.h" | |
19 | ||
20 | using namespace std; | |
21 | ||
22 | template <typename T> | |
23 | typename T::iterator rand_choose(T &cont) { | |
f67539c2 TL |
24 | if (std::empty(cont)) { |
25 | return std::end(cont); | |
7c673cae | 26 | } |
f67539c2 | 27 | return std::next(std::begin(cont), rand() % cont.size()); |
7c673cae FG |
28 | } |
29 | ||
30 | string num_str(unsigned i) { | |
31 | char buf[100]; | |
32 | snprintf(buf, sizeof(buf), "%.10u", i); | |
33 | return string(buf); | |
34 | } | |
35 | ||
36 | class ObjectMapTester { | |
37 | public: | |
38 | ObjectMap *db; | |
39 | set<string> key_space; | |
40 | set<string> object_name_space; | |
41 | map<string, map<string, string> > omap; | |
42 | map<string, string > hmap; | |
43 | map<string, map<string, string> > xattrs; | |
44 | unsigned seq; | |
45 | ||
46 | ObjectMapTester() : db(0), seq(0) {} | |
47 | ||
48 | string val_from_key(const string &object, const string &key) { | |
49 | return object + "_" + key + "_" + num_str(seq++); | |
50 | } | |
51 | ||
52 | void set_key(const string &objname, const string &key, const string &value) { | |
53 | set_key(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), | |
54 | key, value); | |
55 | } | |
56 | ||
57 | void set_xattr(const string &objname, const string &key, const string &value) { | |
58 | set_xattr(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), | |
59 | key, value); | |
60 | } | |
61 | ||
62 | void set_key(ghobject_t hoid, | |
63 | string key, string value) { | |
64 | map<string, bufferlist> to_write; | |
65 | bufferptr bp(value.c_str(), value.size()); | |
66 | bufferlist bl; | |
67 | bl.append(bp); | |
68 | to_write.insert(make_pair(key, bl)); | |
69 | db->set_keys(hoid, to_write); | |
70 | } | |
71 | ||
72 | void set_keys(ghobject_t hoid, const map<string, string> &to_set) { | |
73 | map<string, bufferlist> to_write; | |
74 | for (auto &&i: to_set) { | |
75 | bufferptr bp(i.second.data(), i.second.size()); | |
76 | bufferlist bl; | |
77 | bl.append(bp); | |
78 | to_write.insert(make_pair(i.first, bl)); | |
79 | } | |
80 | db->set_keys(hoid, to_write); | |
81 | } | |
82 | ||
83 | void set_xattr(ghobject_t hoid, | |
84 | string key, string value) { | |
85 | map<string, bufferlist> to_write; | |
86 | bufferptr bp(value.c_str(), value.size()); | |
87 | bufferlist bl; | |
88 | bl.append(bp); | |
89 | to_write.insert(make_pair(key, bl)); | |
90 | db->set_xattrs(hoid, to_write); | |
91 | } | |
92 | ||
93 | void set_header(const string &objname, const string &value) { | |
94 | set_header(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), | |
95 | value); | |
96 | } | |
97 | ||
98 | void set_header(ghobject_t hoid, | |
99 | const string &value) { | |
100 | bufferlist header; | |
101 | header.append(bufferptr(value.c_str(), value.size() + 1)); | |
102 | db->set_header(hoid, header); | |
103 | } | |
104 | ||
105 | int get_header(const string &objname, string *value) { | |
106 | return get_header(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), | |
107 | value); | |
108 | } | |
109 | ||
110 | int get_header(ghobject_t hoid, | |
111 | string *value) { | |
112 | bufferlist header; | |
113 | int r = db->get_header(hoid, &header); | |
114 | if (r < 0) | |
115 | return r; | |
116 | if (header.length()) | |
117 | *value = string(header.c_str()); | |
118 | else | |
119 | *value = string(""); | |
120 | return 0; | |
121 | } | |
122 | ||
123 | int get_xattr(const string &objname, const string &key, string *value) { | |
124 | return get_xattr(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), | |
125 | key, value); | |
126 | } | |
127 | ||
128 | int get_xattr(ghobject_t hoid, | |
129 | string key, string *value) { | |
130 | set<string> to_get; | |
131 | to_get.insert(key); | |
132 | map<string, bufferlist> got; | |
133 | db->get_xattrs(hoid, to_get, &got); | |
134 | if (!got.empty()) { | |
135 | *value = string(got.begin()->second.c_str(), | |
136 | got.begin()->second.length()); | |
137 | return 1; | |
138 | } else { | |
139 | return 0; | |
140 | } | |
141 | } | |
142 | ||
143 | int get_key(const string &objname, const string &key, string *value) { | |
144 | return get_key(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), | |
145 | key, value); | |
146 | } | |
147 | ||
148 | int get_key(ghobject_t hoid, | |
149 | string key, string *value) { | |
150 | set<string> to_get; | |
151 | to_get.insert(key); | |
152 | map<string, bufferlist> got; | |
153 | db->get_values(hoid, to_get, &got); | |
154 | if (!got.empty()) { | |
155 | if (value) { | |
156 | *value = string(got.begin()->second.c_str(), | |
157 | got.begin()->second.length()); | |
158 | } | |
159 | return 1; | |
160 | } else { | |
161 | return 0; | |
162 | } | |
163 | } | |
164 | ||
165 | void remove_key(const string &objname, const string &key) { | |
166 | remove_key(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), | |
167 | key); | |
168 | } | |
169 | ||
170 | void remove_keys(const string &objname, const set<string> &to_remove) { | |
171 | remove_keys(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), | |
172 | to_remove); | |
173 | } | |
174 | ||
175 | void remove_key(ghobject_t hoid, | |
176 | string key) { | |
177 | set<string> to_remove; | |
178 | to_remove.insert(key); | |
179 | db->rm_keys(hoid, to_remove); | |
180 | } | |
181 | ||
182 | void remove_keys(ghobject_t hoid, | |
183 | const set<string> &to_remove) { | |
184 | db->rm_keys(hoid, to_remove); | |
185 | } | |
186 | ||
187 | void remove_xattr(const string &objname, const string &key) { | |
188 | remove_xattr(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), | |
189 | key); | |
190 | } | |
191 | ||
192 | void remove_xattr(ghobject_t hoid, | |
193 | string key) { | |
194 | set<string> to_remove; | |
195 | to_remove.insert(key); | |
196 | db->remove_xattrs(hoid, to_remove); | |
197 | } | |
198 | ||
199 | void clone(const string &objname, const string &target) { | |
200 | clone(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), | |
201 | ghobject_t(hobject_t(sobject_t(target, CEPH_NOSNAP)))); | |
202 | } | |
203 | ||
204 | void clone(ghobject_t hoid, | |
205 | ghobject_t hoid2) { | |
206 | db->clone(hoid, hoid2); | |
207 | } | |
208 | ||
209 | void rename(const string &objname, const string &target) { | |
210 | rename(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), | |
211 | ghobject_t(hobject_t(sobject_t(target, CEPH_NOSNAP)))); | |
212 | } | |
213 | ||
214 | void rename(ghobject_t hoid, | |
215 | ghobject_t hoid2) { | |
216 | db->rename(hoid, hoid2); | |
217 | } | |
218 | ||
219 | void clear(const string &objname) { | |
220 | clear(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP)))); | |
221 | } | |
222 | ||
223 | void legacy_clone(const string &objname, const string &target) { | |
224 | legacy_clone(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), | |
225 | ghobject_t(hobject_t(sobject_t(target, CEPH_NOSNAP)))); | |
226 | } | |
227 | ||
228 | void legacy_clone(ghobject_t hoid, | |
229 | ghobject_t hoid2) { | |
230 | db->legacy_clone(hoid, hoid2); | |
231 | } | |
232 | ||
233 | void clear(ghobject_t hoid) { | |
234 | db->clear(hoid); | |
235 | } | |
236 | ||
237 | void clear_omap(const string &objname) { | |
238 | clear_omap(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP)))); | |
239 | } | |
240 | ||
241 | void clear_omap(const ghobject_t &objname) { | |
242 | db->clear_keys_header(objname); | |
243 | } | |
244 | ||
245 | void def_init() { | |
246 | for (unsigned i = 0; i < 10000; ++i) { | |
247 | key_space.insert("key_" + num_str(i)); | |
248 | } | |
249 | for (unsigned i = 0; i < 100; ++i) { | |
250 | object_name_space.insert("name_" + num_str(i)); | |
251 | } | |
252 | } | |
253 | ||
254 | void init_key_set(const set<string> &keys) { | |
255 | key_space = keys; | |
256 | } | |
257 | ||
258 | void init_object_name_space(const set<string> &onamespace) { | |
259 | object_name_space = onamespace; | |
260 | } | |
261 | ||
262 | void auto_set_xattr(ostream &out) { | |
263 | set<string>::iterator key = rand_choose(key_space); | |
264 | set<string>::iterator object = rand_choose(object_name_space); | |
265 | ||
266 | string value = val_from_key(*object, *key); | |
267 | ||
268 | xattrs[*object][*key] = value; | |
269 | set_xattr(*object, *key, value); | |
270 | ||
271 | out << "auto_set_xattr " << *object << ": " << *key << " -> " | |
272 | << value << std::endl; | |
273 | } | |
274 | ||
275 | void test_set_key(const string &obj, const string &key, const string &val) { | |
276 | omap[obj][key] = val; | |
277 | set_key(obj, key, val); | |
278 | } | |
279 | ||
280 | void test_set_keys(const string &obj, const map<string, string> &to_set) { | |
281 | for (auto &&i: to_set) { | |
282 | omap[obj][i.first] = i.second; | |
283 | } | |
284 | set_keys( | |
285 | ghobject_t(hobject_t(sobject_t(obj, CEPH_NOSNAP))), | |
286 | to_set); | |
287 | } | |
288 | ||
289 | void auto_set_keys(ostream &out) { | |
290 | set<string>::iterator object = rand_choose(object_name_space); | |
291 | ||
292 | map<string, string> to_set; | |
293 | unsigned amount = (rand() % 10) + 1; | |
294 | for (unsigned i = 0; i < amount; ++i) { | |
295 | set<string>::iterator key = rand_choose(key_space); | |
296 | string value = val_from_key(*object, *key); | |
297 | out << "auto_set_key " << *object << ": " << *key << " -> " | |
298 | << value << std::endl; | |
299 | to_set.insert(make_pair(*key, value)); | |
300 | } | |
301 | ||
302 | ||
303 | test_set_keys(*object, to_set); | |
304 | } | |
305 | ||
306 | void xattrs_on_object(const string &object, set<string> *out) { | |
307 | if (!xattrs.count(object)) | |
308 | return; | |
309 | const map<string, string> &xmap = xattrs.find(object)->second; | |
310 | for (map<string, string>::const_iterator i = xmap.begin(); | |
311 | i != xmap.end(); | |
312 | ++i) { | |
313 | out->insert(i->first); | |
314 | } | |
315 | } | |
316 | ||
317 | void keys_on_object(const string &object, set<string> *out) { | |
318 | if (!omap.count(object)) | |
319 | return; | |
320 | const map<string, string> &kmap = omap.find(object)->second; | |
321 | for (map<string, string>::const_iterator i = kmap.begin(); | |
322 | i != kmap.end(); | |
323 | ++i) { | |
324 | out->insert(i->first); | |
325 | } | |
326 | } | |
327 | ||
328 | void xattrs_off_object(const string &object, set<string> *out) { | |
329 | *out = key_space; | |
330 | set<string> xspace; | |
331 | xattrs_on_object(object, &xspace); | |
332 | for (set<string>::iterator i = xspace.begin(); | |
333 | i != xspace.end(); | |
334 | ++i) { | |
335 | out->erase(*i); | |
336 | } | |
337 | } | |
338 | ||
339 | void keys_off_object(const string &object, set<string> *out) { | |
340 | *out = key_space; | |
341 | set<string> kspace; | |
342 | keys_on_object(object, &kspace); | |
343 | for (set<string>::iterator i = kspace.begin(); | |
344 | i != kspace.end(); | |
345 | ++i) { | |
346 | out->erase(*i); | |
347 | } | |
348 | } | |
349 | ||
350 | int auto_check_present_xattr(ostream &out) { | |
351 | set<string>::iterator object = rand_choose(object_name_space); | |
352 | set<string> xspace; | |
353 | xattrs_on_object(*object, &xspace); | |
354 | set<string>::iterator key = rand_choose(xspace); | |
355 | if (key == xspace.end()) { | |
356 | return 1; | |
357 | } | |
358 | ||
359 | string result; | |
360 | int r = get_xattr(*object, *key, &result); | |
361 | if (!r) { | |
362 | out << "auto_check_present_key: failed to find key " | |
363 | << *key << " on object " << *object << std::endl; | |
364 | return 0; | |
365 | } | |
366 | ||
367 | if (result != xattrs[*object][*key]) { | |
368 | out << "auto_check_present_key: for key " | |
369 | << *key << " on object " << *object | |
370 | << " found value " << result << " where we should have found " | |
371 | << xattrs[*object][*key] << std::endl; | |
372 | return 0; | |
373 | } | |
374 | ||
375 | out << "auto_check_present_key: for key " | |
376 | << *key << " on object " << *object | |
377 | << " found value " << result << " where we should have found " | |
378 | << xattrs[*object][*key] << std::endl; | |
379 | return 1; | |
380 | } | |
381 | ||
382 | ||
383 | int auto_check_present_key(ostream &out) { | |
384 | set<string>::iterator object = rand_choose(object_name_space); | |
385 | set<string> kspace; | |
386 | keys_on_object(*object, &kspace); | |
387 | set<string>::iterator key = rand_choose(kspace); | |
388 | if (key == kspace.end()) { | |
389 | return 1; | |
390 | } | |
391 | ||
392 | string result; | |
393 | int r = get_key(*object, *key, &result); | |
394 | if (!r) { | |
395 | out << "auto_check_present_key: failed to find key " | |
396 | << *key << " on object " << *object << std::endl; | |
397 | return 0; | |
398 | } | |
399 | ||
400 | if (result != omap[*object][*key]) { | |
401 | out << "auto_check_present_key: for key " | |
402 | << *key << " on object " << *object | |
403 | << " found value " << result << " where we should have found " | |
404 | << omap[*object][*key] << std::endl; | |
405 | return 0; | |
406 | } | |
407 | ||
408 | out << "auto_check_present_key: for key " | |
409 | << *key << " on object " << *object | |
410 | << " found value " << result << " where we should have found " | |
411 | << omap[*object][*key] << std::endl; | |
412 | return 1; | |
413 | } | |
414 | ||
415 | int auto_check_absent_xattr(ostream &out) { | |
416 | set<string>::iterator object = rand_choose(object_name_space); | |
417 | set<string> xspace; | |
418 | xattrs_off_object(*object, &xspace); | |
419 | set<string>::iterator key = rand_choose(xspace); | |
420 | if (key == xspace.end()) { | |
421 | return 1; | |
422 | } | |
423 | ||
424 | string result; | |
425 | int r = get_xattr(*object, *key, &result); | |
426 | if (!r) { | |
427 | out << "auto_check_absent_key: did not find key " | |
428 | << *key << " on object " << *object << std::endl; | |
429 | return 1; | |
430 | } | |
431 | ||
432 | out << "auto_check_basent_key: for key " | |
433 | << *key << " on object " << *object | |
434 | << " found value " << result << " where we should have found nothing" | |
435 | << std::endl; | |
436 | return 0; | |
437 | } | |
438 | ||
439 | int auto_check_absent_key(ostream &out) { | |
440 | set<string>::iterator object = rand_choose(object_name_space); | |
441 | set<string> kspace; | |
442 | keys_off_object(*object, &kspace); | |
443 | set<string>::iterator key = rand_choose(kspace); | |
444 | if (key == kspace.end()) { | |
445 | return 1; | |
446 | } | |
447 | ||
448 | string result; | |
449 | int r = get_key(*object, *key, &result); | |
450 | if (!r) { | |
451 | out << "auto_check_absent_key: did not find key " | |
452 | << *key << " on object " << *object << std::endl; | |
453 | return 1; | |
454 | } | |
455 | ||
456 | out << "auto_check_basent_key: for key " | |
457 | << *key << " on object " << *object | |
458 | << " found value " << result << " where we should have found nothing" | |
459 | << std::endl; | |
460 | return 0; | |
461 | } | |
462 | ||
463 | void test_clone(const string &object, const string &target, ostream &out) { | |
464 | clone(object, target); | |
465 | if (!omap.count(object)) { | |
466 | out << " source missing."; | |
467 | omap.erase(target); | |
468 | } else { | |
469 | out << " source present."; | |
470 | omap[target] = omap[object]; | |
471 | } | |
472 | if (!hmap.count(object)) { | |
473 | out << " hmap source missing." << std::endl; | |
474 | hmap.erase(target); | |
475 | } else { | |
476 | out << " hmap source present." << std::endl; | |
477 | hmap[target] = hmap[object]; | |
478 | } | |
479 | if (!xattrs.count(object)) { | |
480 | out << " hmap source missing." << std::endl; | |
481 | xattrs.erase(target); | |
482 | } else { | |
483 | out << " hmap source present." << std::endl; | |
484 | xattrs[target] = xattrs[object]; | |
485 | } | |
486 | } | |
487 | ||
488 | void auto_clone_key(ostream &out) { | |
489 | set<string>::iterator object = rand_choose(object_name_space); | |
490 | set<string>::iterator target = rand_choose(object_name_space); | |
491 | while (target == object) { | |
492 | target = rand_choose(object_name_space); | |
493 | } | |
494 | out << "clone " << *object << " to " << *target; | |
495 | test_clone(*object, *target, out); | |
496 | } | |
497 | ||
498 | void test_remove_keys(const string &obj, const set<string> &to_remove) { | |
499 | for (auto &&k: to_remove) | |
500 | omap[obj].erase(k); | |
501 | remove_keys(obj, to_remove); | |
502 | } | |
503 | ||
504 | void test_remove_key(const string &obj, const string &key) { | |
505 | omap[obj].erase(key); | |
506 | remove_key(obj, key); | |
507 | } | |
508 | ||
509 | void auto_remove_keys(ostream &out) { | |
510 | set<string>::iterator object = rand_choose(object_name_space); | |
511 | set<string> kspace; | |
512 | keys_on_object(*object, &kspace); | |
513 | set<string> to_remove; | |
514 | for (unsigned i = 0; i < 3; ++i) { | |
515 | set<string>::iterator key = rand_choose(kspace); | |
516 | if (key == kspace.end()) | |
517 | continue; | |
518 | out << "removing " << *key << " from " << *object << std::endl; | |
519 | to_remove.insert(*key); | |
520 | } | |
521 | test_remove_keys(*object, to_remove); | |
522 | } | |
523 | ||
524 | void auto_remove_xattr(ostream &out) { | |
525 | set<string>::iterator object = rand_choose(object_name_space); | |
526 | set<string> kspace; | |
527 | xattrs_on_object(*object, &kspace); | |
528 | set<string>::iterator key = rand_choose(kspace); | |
529 | if (key == kspace.end()) { | |
530 | return; | |
531 | } | |
532 | out << "removing xattr " << *key << " from " << *object << std::endl; | |
533 | xattrs[*object].erase(*key); | |
534 | remove_xattr(*object, *key); | |
535 | } | |
536 | ||
537 | void auto_delete_object(ostream &out) { | |
538 | set<string>::iterator object = rand_choose(object_name_space); | |
539 | out << "auto_delete_object " << *object << std::endl; | |
540 | clear(*object); | |
541 | omap.erase(*object); | |
542 | hmap.erase(*object); | |
543 | xattrs.erase(*object); | |
544 | } | |
545 | ||
546 | void test_clear(const string &obj) { | |
547 | clear_omap(obj); | |
548 | omap.erase(obj); | |
549 | hmap.erase(obj); | |
550 | } | |
551 | ||
552 | void auto_clear_omap(ostream &out) { | |
553 | set<string>::iterator object = rand_choose(object_name_space); | |
554 | out << "auto_clear_object " << *object << std::endl; | |
555 | test_clear(*object); | |
556 | } | |
557 | ||
558 | void auto_write_header(ostream &out) { | |
559 | set<string>::iterator object = rand_choose(object_name_space); | |
560 | string header = val_from_key(*object, "HEADER"); | |
561 | out << "auto_write_header: " << *object << " -> " << header << std::endl; | |
562 | set_header(*object, header); | |
563 | hmap[*object] = header; | |
564 | } | |
565 | ||
566 | int auto_verify_header(ostream &out) { | |
567 | set<string>::iterator object = rand_choose(object_name_space); | |
568 | out << "verify_header: " << *object << " "; | |
569 | string header; | |
570 | int r = get_header(*object, &header); | |
571 | if (r < 0) { | |
572 | ceph_abort(); | |
573 | } | |
574 | if (header.size() == 0) { | |
575 | if (hmap.count(*object)) { | |
576 | out << " failed to find header " << hmap[*object] << std::endl; | |
577 | return 0; | |
578 | } else { | |
579 | out << " found no header" << std::endl; | |
580 | return 1; | |
581 | } | |
582 | } | |
583 | ||
584 | if (!hmap.count(*object)) { | |
585 | out << " found header " << header << " should have been empty" | |
586 | << std::endl; | |
587 | return 0; | |
588 | } else if (header == hmap[*object]) { | |
589 | out << " found correct header " << header << std::endl; | |
590 | return 1; | |
591 | } else { | |
592 | out << " found incorrect header " << header | |
593 | << " where we should have found " << hmap[*object] << std::endl; | |
594 | return 0; | |
595 | } | |
596 | } | |
597 | ||
598 | void verify_keys(const std::string &obj, ostream &out) { | |
599 | set<string> in_db; | |
600 | ObjectMap::ObjectMapIterator iter = db->get_iterator( | |
601 | ghobject_t(hobject_t(sobject_t(obj, CEPH_NOSNAP)))); | |
602 | for (iter->seek_to_first(); iter->valid(); iter->next()) { | |
603 | in_db.insert(iter->key()); | |
604 | } | |
605 | bool err = false; | |
606 | for (auto &&i: omap[obj]) { | |
607 | if (!in_db.count(i.first)) { | |
608 | out << __func__ << ": obj " << obj << " missing key " | |
609 | << i.first << std::endl; | |
610 | err = true; | |
611 | } else { | |
612 | in_db.erase(i.first); | |
613 | } | |
614 | } | |
615 | if (!in_db.empty()) { | |
616 | out << __func__ << ": obj " << obj << " found extra keys " | |
617 | << in_db << std::endl; | |
618 | err = true; | |
619 | } | |
620 | ASSERT_FALSE(err); | |
621 | } | |
622 | ||
623 | void auto_verify_objects(ostream &out) { | |
624 | for (auto &&i: omap) { | |
625 | verify_keys(i.first, out); | |
626 | } | |
627 | } | |
628 | }; | |
629 | ||
630 | class ObjectMapTest : public ::testing::Test { | |
631 | public: | |
632 | boost::scoped_ptr< ObjectMap > db; | |
633 | ObjectMapTester tester; | |
634 | void SetUp() override { | |
635 | char *path = getenv("OBJECT_MAP_PATH"); | |
636 | if (!path) { | |
637 | db.reset(new DBObjectMap(g_ceph_context, new KeyValueDBMemory())); | |
638 | tester.db = db.get(); | |
639 | return; | |
640 | } | |
641 | ||
642 | string strpath(path); | |
643 | ||
644 | cerr << "using path " << strpath << std::endl; | |
645 | KeyValueDB *store = KeyValueDB::create(g_ceph_context, "leveldb", strpath); | |
11fdf7f2 | 646 | ceph_assert(!store->create_and_open(cerr)); |
7c673cae FG |
647 | |
648 | db.reset(new DBObjectMap(g_ceph_context, store)); | |
649 | tester.db = db.get(); | |
650 | } | |
651 | ||
652 | void TearDown() override { | |
653 | std::cerr << "Checking..." << std::endl; | |
654 | ASSERT_EQ(0, db->check(std::cerr)); | |
655 | } | |
656 | }; | |
657 | ||
658 | ||
659 | int main(int argc, char **argv) { | |
660 | vector<const char*> args; | |
661 | argv_to_vec(argc, (const char **)argv, args); | |
662 | ||
663 | auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, | |
11fdf7f2 TL |
664 | CODE_ENVIRONMENT_UTILITY, |
665 | CINIT_FLAG_NO_DEFAULT_CONFIG_FILE); | |
7c673cae FG |
666 | common_init_finish(g_ceph_context); |
667 | ::testing::InitGoogleTest(&argc, argv); | |
668 | return RUN_ALL_TESTS(); | |
669 | } | |
670 | ||
671 | TEST_F(ObjectMapTest, CreateOneObject) { | |
672 | ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP)), 100, shard_id_t(0)); | |
673 | map<string, bufferlist> to_set; | |
674 | string key("test"); | |
675 | string val("test_val"); | |
676 | bufferptr bp(val.c_str(), val.size()); | |
677 | bufferlist bl; | |
678 | bl.append(bp); | |
679 | to_set.insert(make_pair(key, bl)); | |
31f18b77 | 680 | ASSERT_EQ(db->set_keys(hoid, to_set), 0); |
7c673cae FG |
681 | |
682 | map<string, bufferlist> got; | |
683 | set<string> to_get; | |
684 | to_get.insert(key); | |
685 | to_get.insert("not there"); | |
686 | db->get_values(hoid, to_get, &got); | |
687 | ASSERT_EQ(got.size(), (unsigned)1); | |
688 | ASSERT_EQ(string(got[key].c_str(), got[key].length()), val); | |
689 | ||
690 | bufferlist header; | |
691 | got.clear(); | |
692 | db->get(hoid, &header, &got); | |
693 | ASSERT_EQ(got.size(), (unsigned)1); | |
694 | ASSERT_EQ(string(got[key].c_str(), got[key].length()), val); | |
695 | ASSERT_EQ(header.length(), (unsigned)0); | |
696 | ||
697 | db->rm_keys(hoid, to_get); | |
698 | got.clear(); | |
699 | db->get(hoid, &header, &got); | |
700 | ASSERT_EQ(got.size(), (unsigned)0); | |
701 | ||
702 | map<string, bufferlist> attrs; | |
703 | attrs["attr1"] = bl; | |
704 | db->set_xattrs(hoid, attrs); | |
705 | ||
706 | db->set_header(hoid, bl); | |
707 | ||
708 | db->clear_keys_header(hoid); | |
709 | set<string> attrs_got; | |
710 | db->get_all_xattrs(hoid, &attrs_got); | |
711 | ASSERT_EQ(attrs_got.size(), 1U); | |
712 | ASSERT_EQ(*(attrs_got.begin()), "attr1"); | |
713 | db->get(hoid, &header, &got); | |
714 | ASSERT_EQ(got.size(), (unsigned)0); | |
715 | ASSERT_EQ(header.length(), 0U); | |
716 | got.clear(); | |
717 | ||
718 | db->clear(hoid); | |
719 | db->get(hoid, &header, &got); | |
720 | ASSERT_EQ(got.size(), (unsigned)0); | |
721 | attrs_got.clear(); | |
722 | db->get_all_xattrs(hoid, &attrs_got); | |
723 | ASSERT_EQ(attrs_got.size(), 0U); | |
724 | } | |
725 | ||
726 | TEST_F(ObjectMapTest, CloneOneObject) { | |
727 | ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP)), 200, shard_id_t(0)); | |
728 | ghobject_t hoid2(hobject_t(sobject_t("foo2", CEPH_NOSNAP)), 201, shard_id_t(1)); | |
729 | ||
730 | tester.set_key(hoid, "foo", "bar"); | |
731 | tester.set_key(hoid, "foo2", "bar2"); | |
732 | string result; | |
733 | int r = tester.get_key(hoid, "foo", &result); | |
734 | ASSERT_EQ(r, 1); | |
735 | ASSERT_EQ(result, "bar"); | |
736 | ||
737 | db->clone(hoid, hoid2); | |
738 | r = tester.get_key(hoid, "foo", &result); | |
739 | ASSERT_EQ(r, 1); | |
740 | ASSERT_EQ(result, "bar"); | |
741 | r = tester.get_key(hoid2, "foo", &result); | |
742 | ASSERT_EQ(r, 1); | |
743 | ASSERT_EQ(result, "bar"); | |
744 | ||
745 | tester.remove_key(hoid, "foo"); | |
746 | r = tester.get_key(hoid2, "foo", &result); | |
747 | ASSERT_EQ(r, 1); | |
748 | ASSERT_EQ(result, "bar"); | |
749 | r = tester.get_key(hoid, "foo", &result); | |
750 | ASSERT_EQ(r, 0); | |
751 | r = tester.get_key(hoid, "foo2", &result); | |
752 | ASSERT_EQ(r, 1); | |
753 | ASSERT_EQ(result, "bar2"); | |
754 | ||
755 | tester.set_key(hoid, "foo", "baz"); | |
756 | tester.remove_key(hoid, "foo"); | |
757 | r = tester.get_key(hoid, "foo", &result); | |
758 | ASSERT_EQ(r, 0); | |
759 | ||
760 | tester.set_key(hoid, "foo2", "baz"); | |
761 | tester.remove_key(hoid, "foo2"); | |
762 | r = tester.get_key(hoid, "foo2", &result); | |
763 | ASSERT_EQ(r, 0); | |
764 | ||
765 | map<string, bufferlist> got; | |
766 | bufferlist header; | |
767 | ||
768 | got.clear(); | |
769 | db->clear(hoid); | |
770 | db->get(hoid, &header, &got); | |
771 | ASSERT_EQ(got.size(), (unsigned)0); | |
772 | ||
773 | got.clear(); | |
774 | r = db->clear(hoid2); | |
775 | ASSERT_EQ(0, r); | |
776 | db->get(hoid2, &header, &got); | |
777 | ASSERT_EQ(got.size(), (unsigned)0); | |
778 | ||
779 | tester.set_key(hoid, "baz", "bar"); | |
780 | got.clear(); | |
781 | db->get(hoid, &header, &got); | |
782 | ASSERT_EQ(got.size(), (unsigned)1); | |
783 | db->clear(hoid); | |
784 | db->clear(hoid2); | |
785 | } | |
786 | ||
787 | TEST_F(ObjectMapTest, OddEvenClone) { | |
788 | ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP))); | |
789 | ghobject_t hoid2(hobject_t(sobject_t("foo2", CEPH_NOSNAP))); | |
790 | ||
791 | for (unsigned i = 0; i < 1000; ++i) { | |
792 | tester.set_key(hoid, "foo" + num_str(i), "bar" + num_str(i)); | |
793 | } | |
794 | ||
795 | db->clone(hoid, hoid2); | |
796 | ||
797 | int r = 0; | |
798 | for (unsigned i = 0; i < 1000; ++i) { | |
799 | string result; | |
800 | r = tester.get_key(hoid, "foo" + num_str(i), &result); | |
801 | ASSERT_EQ(1, r); | |
802 | ASSERT_EQ("bar" + num_str(i), result); | |
803 | r = tester.get_key(hoid2, "foo" + num_str(i), &result); | |
804 | ASSERT_EQ(1, r); | |
805 | ASSERT_EQ("bar" + num_str(i), result); | |
806 | ||
807 | if (i % 2) { | |
808 | tester.remove_key(hoid, "foo" + num_str(i)); | |
809 | } else { | |
810 | tester.remove_key(hoid2, "foo" + num_str(i)); | |
811 | } | |
812 | } | |
813 | ||
814 | for (unsigned i = 0; i < 1000; ++i) { | |
815 | string result; | |
816 | string result2; | |
817 | r = tester.get_key(hoid, "foo" + num_str(i), &result); | |
818 | int r2 = tester.get_key(hoid2, "foo" + num_str(i), &result2); | |
819 | if (i % 2) { | |
820 | ASSERT_EQ(0, r); | |
821 | ASSERT_EQ(1, r2); | |
822 | ASSERT_EQ("bar" + num_str(i), result2); | |
823 | } else { | |
824 | ASSERT_EQ(0, r2); | |
825 | ASSERT_EQ(1, r); | |
826 | ASSERT_EQ("bar" + num_str(i), result); | |
827 | } | |
828 | } | |
829 | ||
830 | { | |
831 | ObjectMap::ObjectMapIterator iter = db->get_iterator(hoid); | |
832 | iter->seek_to_first(); | |
833 | for (unsigned i = 0; i < 1000; ++i) { | |
834 | if (!(i % 2)) { | |
835 | ASSERT_TRUE(iter->valid()); | |
836 | ASSERT_EQ("foo" + num_str(i), iter->key()); | |
837 | iter->next(); | |
838 | } | |
839 | } | |
840 | } | |
841 | ||
842 | { | |
843 | ObjectMap::ObjectMapIterator iter2 = db->get_iterator(hoid2); | |
844 | iter2->seek_to_first(); | |
845 | for (unsigned i = 0; i < 1000; ++i) { | |
846 | if (i % 2) { | |
847 | ASSERT_TRUE(iter2->valid()); | |
848 | ASSERT_EQ("foo" + num_str(i), iter2->key()); | |
849 | iter2->next(); | |
850 | } | |
851 | } | |
852 | } | |
853 | ||
854 | db->clear(hoid); | |
855 | db->clear(hoid2); | |
856 | } | |
857 | ||
858 | TEST_F(ObjectMapTest, Rename) { | |
859 | ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP))); | |
860 | ghobject_t hoid2(hobject_t(sobject_t("foo2", CEPH_NOSNAP))); | |
861 | ||
862 | for (unsigned i = 0; i < 1000; ++i) { | |
863 | tester.set_key(hoid, "foo" + num_str(i), "bar" + num_str(i)); | |
864 | } | |
865 | ||
866 | db->rename(hoid, hoid2); | |
867 | // Verify rename where target exists | |
868 | db->clone(hoid2, hoid); | |
869 | db->rename(hoid, hoid2); | |
870 | ||
871 | int r = 0; | |
872 | for (unsigned i = 0; i < 1000; ++i) { | |
873 | string result; | |
874 | r = tester.get_key(hoid2, "foo" + num_str(i), &result); | |
875 | ASSERT_EQ(1, r); | |
876 | ASSERT_EQ("bar" + num_str(i), result); | |
877 | ||
878 | if (i % 2) { | |
879 | tester.remove_key(hoid2, "foo" + num_str(i)); | |
880 | } | |
881 | } | |
882 | ||
883 | for (unsigned i = 0; i < 1000; ++i) { | |
884 | string result; | |
885 | r = tester.get_key(hoid2, "foo" + num_str(i), &result); | |
886 | if (i % 2) { | |
887 | ASSERT_EQ(0, r); | |
888 | } else { | |
889 | ASSERT_EQ(1, r); | |
890 | ASSERT_EQ("bar" + num_str(i), result); | |
891 | } | |
892 | } | |
893 | ||
894 | { | |
895 | ObjectMap::ObjectMapIterator iter = db->get_iterator(hoid2); | |
896 | iter->seek_to_first(); | |
897 | for (unsigned i = 0; i < 1000; ++i) { | |
898 | if (!(i % 2)) { | |
899 | ASSERT_TRUE(iter->valid()); | |
900 | ASSERT_EQ("foo" + num_str(i), iter->key()); | |
901 | iter->next(); | |
902 | } | |
903 | } | |
904 | } | |
905 | ||
906 | db->clear(hoid2); | |
907 | } | |
908 | ||
909 | TEST_F(ObjectMapTest, OddEvenOldClone) { | |
910 | ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP))); | |
911 | ghobject_t hoid2(hobject_t(sobject_t("foo2", CEPH_NOSNAP))); | |
912 | ||
913 | for (unsigned i = 0; i < 1000; ++i) { | |
914 | tester.set_key(hoid, "foo" + num_str(i), "bar" + num_str(i)); | |
915 | } | |
916 | ||
917 | db->legacy_clone(hoid, hoid2); | |
918 | ||
919 | int r = 0; | |
920 | for (unsigned i = 0; i < 1000; ++i) { | |
921 | string result; | |
922 | r = tester.get_key(hoid, "foo" + num_str(i), &result); | |
923 | ASSERT_EQ(1, r); | |
924 | ASSERT_EQ("bar" + num_str(i), result); | |
925 | r = tester.get_key(hoid2, "foo" + num_str(i), &result); | |
926 | ASSERT_EQ(1, r); | |
927 | ASSERT_EQ("bar" + num_str(i), result); | |
928 | ||
929 | if (i % 2) { | |
930 | tester.remove_key(hoid, "foo" + num_str(i)); | |
931 | } else { | |
932 | tester.remove_key(hoid2, "foo" + num_str(i)); | |
933 | } | |
934 | } | |
935 | ||
936 | for (unsigned i = 0; i < 1000; ++i) { | |
937 | string result; | |
938 | string result2; | |
939 | r = tester.get_key(hoid, "foo" + num_str(i), &result); | |
940 | int r2 = tester.get_key(hoid2, "foo" + num_str(i), &result2); | |
941 | if (i % 2) { | |
942 | ASSERT_EQ(0, r); | |
943 | ASSERT_EQ(1, r2); | |
944 | ASSERT_EQ("bar" + num_str(i), result2); | |
945 | } else { | |
946 | ASSERT_EQ(0, r2); | |
947 | ASSERT_EQ(1, r); | |
948 | ASSERT_EQ("bar" + num_str(i), result); | |
949 | } | |
950 | } | |
951 | ||
952 | { | |
953 | ObjectMap::ObjectMapIterator iter = db->get_iterator(hoid); | |
954 | iter->seek_to_first(); | |
955 | for (unsigned i = 0; i < 1000; ++i) { | |
956 | if (!(i % 2)) { | |
957 | ASSERT_TRUE(iter->valid()); | |
958 | ASSERT_EQ("foo" + num_str(i), iter->key()); | |
959 | iter->next(); | |
960 | } | |
961 | } | |
962 | } | |
963 | ||
964 | { | |
965 | ObjectMap::ObjectMapIterator iter2 = db->get_iterator(hoid2); | |
966 | iter2->seek_to_first(); | |
967 | for (unsigned i = 0; i < 1000; ++i) { | |
968 | if (i % 2) { | |
969 | ASSERT_TRUE(iter2->valid()); | |
970 | ASSERT_EQ("foo" + num_str(i), iter2->key()); | |
971 | iter2->next(); | |
972 | } | |
973 | } | |
974 | } | |
975 | ||
976 | db->clear(hoid); | |
977 | db->clear(hoid2); | |
978 | } | |
979 | ||
980 | TEST_F(ObjectMapTest, RandomTest) { | |
981 | tester.def_init(); | |
982 | for (unsigned i = 0; i < 5000; ++i) { | |
983 | unsigned val = rand(); | |
984 | val <<= 8; | |
985 | val %= 100; | |
986 | if (!(i%100)) | |
987 | std::cout << "on op " << i | |
988 | << " val is " << val << std::endl; | |
989 | ||
990 | if (val < 7) { | |
991 | tester.auto_write_header(std::cerr); | |
992 | } else if (val < 14) { | |
993 | ASSERT_TRUE(tester.auto_verify_header(std::cerr)); | |
994 | } else if (val < 30) { | |
995 | tester.auto_set_keys(std::cerr); | |
996 | } else if (val < 42) { | |
997 | tester.auto_set_xattr(std::cerr); | |
998 | } else if (val < 55) { | |
999 | ASSERT_TRUE(tester.auto_check_present_key(std::cerr)); | |
1000 | } else if (val < 62) { | |
1001 | ASSERT_TRUE(tester.auto_check_present_xattr(std::cerr)); | |
1002 | } else if (val < 70) { | |
1003 | ASSERT_TRUE(tester.auto_check_absent_key(std::cerr)); | |
1004 | } else if (val < 72) { | |
1005 | ASSERT_TRUE(tester.auto_check_absent_xattr(std::cerr)); | |
1006 | } else if (val < 73) { | |
1007 | tester.auto_clear_omap(std::cerr); | |
1008 | } else if (val < 76) { | |
1009 | tester.auto_delete_object(std::cerr); | |
1010 | } else if (val < 85) { | |
1011 | tester.auto_clone_key(std::cerr); | |
1012 | } else if (val < 92) { | |
1013 | tester.auto_remove_xattr(std::cerr); | |
1014 | } else { | |
1015 | tester.auto_remove_keys(std::cerr); | |
1016 | } | |
1017 | ||
1018 | if (i % 500) { | |
1019 | tester.auto_verify_objects(std::cerr); | |
1020 | } | |
1021 | } | |
1022 | } | |
1023 | ||
1024 | TEST_F(ObjectMapTest, RandomTestNoDeletesXattrs) { | |
1025 | tester.def_init(); | |
1026 | for (unsigned i = 0; i < 5000; ++i) { | |
1027 | unsigned val = rand(); | |
1028 | val <<= 8; | |
1029 | val %= 100; | |
1030 | if (!(i%100)) | |
1031 | std::cout << "on op " << i | |
1032 | << " val is " << val << std::endl; | |
1033 | ||
1034 | if (val < 45) { | |
1035 | tester.auto_set_keys(std::cerr); | |
1036 | } else if (val < 90) { | |
1037 | tester.auto_remove_keys(std::cerr); | |
1038 | } else { | |
1039 | tester.auto_clone_key(std::cerr); | |
1040 | } | |
1041 | ||
1042 | if (i % 500) { | |
1043 | tester.auto_verify_objects(std::cerr); | |
1044 | } | |
1045 | } | |
1046 | } | |
1047 | ||
1048 | string num_to_key(unsigned i) { | |
1049 | char buf[100]; | |
1050 | int ret = snprintf(buf, sizeof(buf), "%010u", i); | |
11fdf7f2 | 1051 | ceph_assert(ret > 0); |
7c673cae FG |
1052 | return string(buf, ret); |
1053 | } | |
1054 | ||
1055 | TEST_F(ObjectMapTest, TestMergeNewCompleteContainBug) { | |
1056 | /* This test exploits a bug in kraken and earlier where merge_new_complete | |
1057 | * could miss complete entries fully contained by a new entry. To get this | |
1058 | * to actually result in an incorrect return value, you need to remove at | |
1059 | * least two values, one before a complete region, and one which occurs in | |
1060 | * the parent after the complete region (but within 20 not yet completed | |
1061 | * parent points of the first value). | |
1062 | */ | |
1063 | for (unsigned i = 10; i < 160; i+=2) { | |
1064 | tester.test_set_key("foo", num_to_key(i), "asdf"); | |
1065 | } | |
1066 | tester.test_clone("foo", "foo2", std::cout); | |
1067 | tester.test_clear("foo"); | |
1068 | ||
1069 | tester.test_set_key("foo2", num_to_key(15), "asdf"); | |
1070 | tester.test_set_key("foo2", num_to_key(13), "asdf"); | |
1071 | tester.test_set_key("foo2", num_to_key(57), "asdf"); | |
1072 | ||
1073 | tester.test_remove_key("foo2", num_to_key(15)); | |
1074 | ||
1075 | set<string> to_remove; | |
1076 | to_remove.insert(num_to_key(13)); | |
1077 | to_remove.insert(num_to_key(58)); | |
1078 | to_remove.insert(num_to_key(60)); | |
1079 | to_remove.insert(num_to_key(62)); | |
1080 | tester.test_remove_keys("foo2", to_remove); | |
1081 | ||
1082 | tester.verify_keys("foo2", std::cout); | |
1083 | ASSERT_EQ(tester.get_key("foo2", num_to_key(10), nullptr), 1); | |
1084 | ASSERT_EQ(tester.get_key("foo2", num_to_key(1), nullptr), 0); | |
1085 | ASSERT_EQ(tester.get_key("foo2", num_to_key(56), nullptr), 1); | |
1086 | // this one triggers the bug | |
1087 | ASSERT_EQ(tester.get_key("foo2", num_to_key(58), nullptr), 0); | |
1088 | } | |
1089 | ||
1090 | TEST_F(ObjectMapTest, TestIterateBug18533) { | |
1091 | /* This test starts with the one immediately above to create a pair of | |
1092 | * complete regions where one contains the other. Then, it deletes the | |
1093 | * key at the start of the contained region. The logic in next_parent() | |
1094 | * skips ahead to the end of the contained region, and we start copying | |
1095 | * values down again from the parent into the child -- including some | |
1096 | * that had actually been deleted. I think this works for any removal | |
1097 | * within the outer complete region after the start of the contained | |
1098 | * region. | |
1099 | */ | |
1100 | for (unsigned i = 10; i < 160; i+=2) { | |
1101 | tester.test_set_key("foo", num_to_key(i), "asdf"); | |
1102 | } | |
1103 | tester.test_clone("foo", "foo2", std::cout); | |
1104 | tester.test_clear("foo"); | |
1105 | ||
1106 | tester.test_set_key("foo2", num_to_key(15), "asdf"); | |
1107 | tester.test_set_key("foo2", num_to_key(13), "asdf"); | |
1108 | tester.test_set_key("foo2", num_to_key(57), "asdf"); | |
1109 | tester.test_set_key("foo2", num_to_key(91), "asdf"); | |
1110 | ||
1111 | tester.test_remove_key("foo2", num_to_key(15)); | |
1112 | ||
1113 | set<string> to_remove; | |
1114 | to_remove.insert(num_to_key(13)); | |
1115 | to_remove.insert(num_to_key(58)); | |
1116 | to_remove.insert(num_to_key(60)); | |
1117 | to_remove.insert(num_to_key(62)); | |
1118 | to_remove.insert(num_to_key(82)); | |
1119 | to_remove.insert(num_to_key(84)); | |
1120 | tester.test_remove_keys("foo2", to_remove); | |
1121 | ||
1122 | //tester.test_remove_key("foo2", num_to_key(15)); also does the trick | |
1123 | tester.test_remove_key("foo2", num_to_key(80)); | |
1124 | ||
1125 | // the iterator in verify_keys will return an extra value | |
1126 | tester.verify_keys("foo2", std::cout); | |
1127 | } | |
1128 |