]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/test_snap_mapper.cc
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / test / test_snap_mapper.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 #include "include/memory.h"
3 #include <map>
4 #include <set>
5 #include <boost/scoped_ptr.hpp>
6 #include <sys/types.h>
7 #include <cstdlib>
8
9 #include "include/buffer.h"
10 #include "common/map_cacher.hpp"
11 #include "osd/SnapMapper.h"
12
13 #include "gtest/gtest.h"
14
15 using namespace std;
16
17 template <typename T>
18 typename T::iterator rand_choose(T &cont) {
19 if (cont.size() == 0) {
20 return cont.end();
21 }
22 int index = rand() % cont.size();
23 typename T::iterator retval = cont.begin();
24
25 for (; index > 0; --index) ++retval;
26 return retval;
27 }
28
29 string random_string(size_t size)
30 {
31 string name;
32 for (size_t j = 0; j < size; ++j) {
33 name.push_back('a' + (rand() % 26));
34 }
35 return name;
36 }
37
38 class PausyAsyncMap : public MapCacher::StoreDriver<string, bufferlist> {
39 struct _Op {
40 virtual void operate(map<string, bufferlist> *store) = 0;
41 virtual ~_Op() {}
42 };
43 typedef ceph::shared_ptr<_Op> Op;
44 struct Remove : public _Op {
45 set<string> to_remove;
46 explicit Remove(const set<string> &to_remove) : to_remove(to_remove) {}
47 void operate(map<string, bufferlist> *store) override {
48 for (set<string>::iterator i = to_remove.begin();
49 i != to_remove.end();
50 ++i) {
51 store->erase(*i);
52 }
53 }
54 };
55 struct Insert : public _Op {
56 map<string, bufferlist> to_insert;
57 explicit Insert(const map<string, bufferlist> &to_insert) : to_insert(to_insert) {}
58 void operate(map<string, bufferlist> *store) override {
59 for (map<string, bufferlist>::iterator i = to_insert.begin();
60 i != to_insert.end();
61 ++i) {
62 store->erase(i->first);
63 store->insert(*i);
64 }
65 }
66 };
67 struct Callback : public _Op {
68 Context *context;
69 explicit Callback(Context *c) : context(c) {}
70 void operate(map<string, bufferlist> *store) override {
71 context->complete(0);
72 }
73 };
74 public:
75 class Transaction : public MapCacher::Transaction<string, bufferlist> {
76 friend class PausyAsyncMap;
77 list<Op> ops;
78 list<Op> callbacks;
79 public:
80 void set_keys(const map<string, bufferlist> &i) override {
81 ops.push_back(Op(new Insert(i)));
82 }
83 void remove_keys(const set<string> &r) override {
84 ops.push_back(Op(new Remove(r)));
85 }
86 void add_callback(Context *c) override {
87 callbacks.push_back(Op(new Callback(c)));
88 }
89 };
90 private:
91
92 Mutex lock;
93 map<string, bufferlist> store;
94
95 class Doer : public Thread {
96 static const size_t MAX_SIZE = 100;
97 PausyAsyncMap *parent;
98 Mutex lock;
99 Cond cond;
100 int stopping;
101 bool paused;
102 list<Op> queue;
103 public:
104 explicit Doer(PausyAsyncMap *parent) :
105 parent(parent), lock("Doer lock"), stopping(0), paused(false) {}
106 void *entry() override {
107 while (1) {
108 list<Op> ops;
109 {
110 Mutex::Locker l(lock);
111 while (!stopping && (queue.empty() || paused))
112 cond.Wait(lock);
113 if (stopping && queue.empty()) {
114 stopping = 2;
115 cond.Signal();
116 return 0;
117 }
118 assert(!queue.empty());
119 assert(!paused);
120 ops.swap(queue);
121 cond.Signal();
122 }
123 assert(!ops.empty());
124
125 for (list<Op>::iterator i = ops.begin();
126 i != ops.end();
127 ops.erase(i++)) {
128 if (!(rand()%3))
129 usleep(1+(rand() % 5000));
130 Mutex::Locker l(parent->lock);
131 (*i)->operate(&(parent->store));
132 }
133 }
134 }
135
136 void pause() {
137 Mutex::Locker l(lock);
138 paused = true;
139 cond.Signal();
140 }
141
142 void resume() {
143 Mutex::Locker l(lock);
144 paused = false;
145 cond.Signal();
146 }
147
148 void submit(list<Op> &in) {
149 Mutex::Locker l(lock);
150 while (queue.size() >= MAX_SIZE)
151 cond.Wait(lock);
152 queue.splice(queue.end(), in, in.begin(), in.end());
153 cond.Signal();
154 }
155
156 void stop() {
157 Mutex::Locker l(lock);
158 stopping = 1;
159 cond.Signal();
160 while (stopping != 2)
161 cond.Wait(lock);
162 cond.Signal();
163 }
164 } doer;
165
166 public:
167 PausyAsyncMap() : lock("PausyAsyncMap"), doer(this) {
168 doer.create("doer");
169 }
170 ~PausyAsyncMap() override {
171 doer.join();
172 }
173 int get_keys(
174 const set<string> &keys,
175 map<string, bufferlist> *out) override {
176 Mutex::Locker l(lock);
177 for (set<string>::const_iterator i = keys.begin();
178 i != keys.end();
179 ++i) {
180 map<string, bufferlist>::iterator j = store.find(*i);
181 if (j != store.end())
182 out->insert(*j);
183 }
184 return 0;
185 }
186 int get_next(
187 const string &key,
188 pair<string, bufferlist> *next) override {
189 Mutex::Locker l(lock);
190 map<string, bufferlist>::iterator j = store.upper_bound(key);
191 if (j != store.end()) {
192 if (next)
193 *next = *j;
194 return 0;
195 } else {
196 return -ENOENT;
197 }
198 }
199 void submit(Transaction *t) {
200 doer.submit(t->ops);
201 doer.submit(t->callbacks);
202 }
203
204 void flush() {
205 Mutex lock("flush lock");
206 Cond cond;
207 bool done = false;
208
209 class OnFinish : public Context {
210 Mutex *lock;
211 Cond *cond;
212 bool *done;
213 public:
214 OnFinish(Mutex *lock, Cond *cond, bool *done)
215 : lock(lock), cond(cond), done(done) {}
216 void finish(int) override {
217 Mutex::Locker l(*lock);
218 *done = true;
219 cond->Signal();
220 }
221 };
222 Transaction t;
223 t.add_callback(new OnFinish(&lock, &cond, &done));
224 submit(&t);
225 {
226 Mutex::Locker l(lock);
227 while (!done)
228 cond.Wait(lock);
229 }
230 }
231
232 void pause() {
233 doer.pause();
234 }
235 void resume() {
236 doer.resume();
237 }
238 void stop() {
239 doer.stop();
240 }
241
242 };
243
244 class MapCacherTest : public ::testing::Test {
245 protected:
246 boost::scoped_ptr< PausyAsyncMap > driver;
247 boost::scoped_ptr<MapCacher::MapCacher<string, bufferlist> > cache;
248 map<string, bufferlist> truth;
249 set<string> names;
250 public:
251 void assert_bl_eq(bufferlist &bl1, bufferlist &bl2) {
252 ASSERT_EQ(bl1.length(), bl2.length());
253 bufferlist::iterator j = bl2.begin();
254 for (bufferlist::iterator i = bl1.begin();
255 !i.end();
256 ++i, ++j) {
257 ASSERT_TRUE(!j.end());
258 ASSERT_EQ(*i, *j);
259 }
260 }
261 void assert_bl_map_eq(map<string, bufferlist> &m1,
262 map<string, bufferlist> &m2) {
263 ASSERT_EQ(m1.size(), m2.size());
264 map<string, bufferlist>::iterator j = m2.begin();
265 for (map<string, bufferlist>::iterator i = m1.begin();
266 i != m1.end();
267 ++i, ++j) {
268 ASSERT_TRUE(j != m2.end());
269 ASSERT_EQ(i->first, j->first);
270 assert_bl_eq(i->second, j->second);
271 }
272
273 }
274 size_t random_num() {
275 return random() % 10;
276 }
277 size_t random_size() {
278 return random() % 1000;
279 }
280 void random_bl(size_t size, bufferlist *bl) {
281 for (size_t i = 0; i < size; ++i) {
282 bl->append(rand());
283 }
284 }
285 void do_set() {
286 size_t set_size = random_num();
287 map<string, bufferlist> to_set;
288 for (size_t i = 0; i < set_size; ++i) {
289 bufferlist bl;
290 random_bl(random_size(), &bl);
291 string key = *rand_choose(names);
292 to_set.insert(
293 make_pair(key, bl));
294 }
295 for (map<string, bufferlist>::iterator i = to_set.begin();
296 i != to_set.end();
297 ++i) {
298 truth.erase(i->first);
299 truth.insert(*i);
300 }
301 {
302 PausyAsyncMap::Transaction t;
303 cache->set_keys(to_set, &t);
304 driver->submit(&t);
305 }
306 }
307 void remove() {
308 size_t remove_size = random_num();
309 set<string> to_remove;
310 for (size_t i = 0; i < remove_size ; ++i) {
311 to_remove.insert(*rand_choose(names));
312 }
313 for (set<string>::iterator i = to_remove.begin();
314 i != to_remove.end();
315 ++i) {
316 truth.erase(*i);
317 }
318 {
319 PausyAsyncMap::Transaction t;
320 cache->remove_keys(to_remove, &t);
321 driver->submit(&t);
322 }
323 }
324 void get() {
325 set<string> to_get;
326 size_t get_size = random_num();
327 for (size_t i = 0; i < get_size; ++i) {
328 to_get.insert(*rand_choose(names));
329 }
330
331 map<string, bufferlist> got_truth;
332 for (set<string>::iterator i = to_get.begin();
333 i != to_get.end();
334 ++i) {
335 map<string, bufferlist>::iterator j = truth.find(*i);
336 if (j != truth.end())
337 got_truth.insert(*j);
338 }
339
340 map<string, bufferlist> got;
341 cache->get_keys(to_get, &got);
342
343 assert_bl_map_eq(got, got_truth);
344 }
345
346 void get_next() {
347 string cur;
348 while (true) {
349 pair<string, bufferlist> next;
350 int r = cache->get_next(cur, &next);
351
352 pair<string, bufferlist> next_truth;
353 map<string, bufferlist>::iterator i = truth.upper_bound(cur);
354 int r_truth = (i == truth.end()) ? -ENOENT : 0;
355 if (i != truth.end())
356 next_truth = *i;
357
358 ASSERT_EQ(r, r_truth);
359 if (r == -ENOENT)
360 break;
361
362 ASSERT_EQ(next.first, next_truth.first);
363 assert_bl_eq(next.second, next_truth.second);
364 cur = next.first;
365 }
366 }
367 void SetUp() override {
368 driver.reset(new PausyAsyncMap());
369 cache.reset(new MapCacher::MapCacher<string, bufferlist>(driver.get()));
370 names.clear();
371 truth.clear();
372 size_t names_size(random_num() + 10);
373 for (size_t i = 0; i < names_size; ++i) {
374 names.insert(random_string(1 + (random_size() % 10)));
375 }
376 }
377 void TearDown() override {
378 driver->stop();
379 cache.reset();
380 driver.reset();
381 }
382
383 };
384
385 TEST_F(MapCacherTest, Simple)
386 {
387 driver->pause();
388 map<string, bufferlist> truth;
389 set<string> truth_keys;
390 string blah("asdf");
391 bufferlist bl;
392 ::encode(blah, bl);
393 truth[string("asdf")] = bl;
394 truth_keys.insert(truth.begin()->first);
395 {
396 PausyAsyncMap::Transaction t;
397 cache->set_keys(truth, &t);
398 driver->submit(&t);
399 cache->set_keys(truth, &t);
400 driver->submit(&t);
401 }
402
403 map<string, bufferlist> got;
404 cache->get_keys(truth_keys, &got);
405 assert_bl_map_eq(got, truth);
406
407 driver->resume();
408 sleep(1);
409
410 got.clear();
411 cache->get_keys(truth_keys, &got);
412 assert_bl_map_eq(got, truth);
413 }
414
415 TEST_F(MapCacherTest, Random)
416 {
417 for (size_t i = 0; i < 5000; ++i) {
418 if (!(i % 50)) {
419 std::cout << "On iteration " << i << std::endl;
420 }
421 switch (rand() % 4) {
422 case 0:
423 get();
424 break;
425 case 1:
426 do_set();
427 break;
428 case 2:
429 get_next();
430 break;
431 case 3:
432 remove();
433 break;
434 }
435 }
436 }
437
438 class MapperVerifier {
439 PausyAsyncMap *driver;
440 boost::scoped_ptr< SnapMapper > mapper;
441 map<snapid_t, set<hobject_t> > snap_to_hobject;
442 map<hobject_t, set<snapid_t>> hobject_to_snap;
443 snapid_t next;
444 uint32_t mask;
445 uint32_t bits;
446 Mutex lock;
447 public:
448
449 MapperVerifier(
450 PausyAsyncMap *driver,
451 uint32_t mask,
452 uint32_t bits)
453 : driver(driver),
454 mapper(new SnapMapper(g_ceph_context, driver, mask, bits, 0, shard_id_t(1))),
455 mask(mask), bits(bits),
456 lock("lock") {}
457
458 hobject_t random_hobject() {
459 return hobject_t(
460 random_string(1+(rand() % 16)),
461 random_string(1+(rand() % 16)),
462 snapid_t(rand() % 1000),
463 (rand() & ((~0)<<bits)) | (mask & ~((~0)<<bits)),
464 0, random_string(rand() % 16));
465 }
466
467 void choose_random_snaps(int num, set<snapid_t> *snaps) {
468 assert(snaps);
469 assert(!snap_to_hobject.empty());
470 for (int i = 0; i < num || snaps->empty(); ++i) {
471 snaps->insert(rand_choose(snap_to_hobject)->first);
472 }
473 }
474
475 void create_snap() {
476 snap_to_hobject[next];
477 ++next;
478 }
479
480 void create_object() {
481 Mutex::Locker l(lock);
482 if (snap_to_hobject.empty())
483 return;
484 hobject_t obj;
485 do {
486 obj = random_hobject();
487 } while (hobject_to_snap.count(obj));
488
489 set<snapid_t> &snaps = hobject_to_snap[obj];
490 choose_random_snaps(1 + (rand() % 20), &snaps);
491 for (set<snapid_t>::iterator i = snaps.begin();
492 i != snaps.end();
493 ++i) {
494 map<snapid_t, set<hobject_t> >::iterator j = snap_to_hobject.find(*i);
495 assert(j != snap_to_hobject.end());
496 j->second.insert(obj);
497 }
498 {
499 PausyAsyncMap::Transaction t;
500 mapper->add_oid(obj, snaps, &t);
501 driver->submit(&t);
502 }
503 }
504
505 void trim_snap() {
506 Mutex::Locker l(lock);
507 if (snap_to_hobject.empty())
508 return;
509 map<snapid_t, set<hobject_t> >::iterator snap =
510 rand_choose(snap_to_hobject);
511 set<hobject_t> hobjects = snap->second;
512
513 vector<hobject_t> hoids;
514 while (mapper->get_next_objects_to_trim(
515 snap->first, rand() % 5 + 1, &hoids) == 0) {
516 for (auto &&hoid: hoids) {
517 assert(!hoid.is_max());
518 assert(hobjects.count(hoid));
519 hobjects.erase(hoid);
520
521 map<hobject_t, set<snapid_t>>::iterator j =
522 hobject_to_snap.find(hoid);
523 assert(j->second.count(snap->first));
524 set<snapid_t> old_snaps(j->second);
525 j->second.erase(snap->first);
526
527 {
528 PausyAsyncMap::Transaction t;
529 mapper->update_snaps(
530 hoid,
531 j->second,
532 &old_snaps,
533 &t);
534 driver->submit(&t);
535 }
536 if (j->second.empty()) {
537 hobject_to_snap.erase(j);
538 }
539 hoid = hobject_t::get_max();
540 }
541 hoids.clear();
542 }
543 assert(hobjects.empty());
544 snap_to_hobject.erase(snap);
545 }
546
547 void remove_oid() {
548 Mutex::Locker l(lock);
549 if (hobject_to_snap.empty())
550 return;
551 map<hobject_t, set<snapid_t>>::iterator obj =
552 rand_choose(hobject_to_snap);
553 for (set<snapid_t>::iterator i = obj->second.begin();
554 i != obj->second.end();
555 ++i) {
556 map<snapid_t, set<hobject_t> >::iterator j =
557 snap_to_hobject.find(*i);
558 assert(j->second.count(obj->first));
559 j->second.erase(obj->first);
560 }
561 {
562 PausyAsyncMap::Transaction t;
563 mapper->remove_oid(
564 obj->first,
565 &t);
566 driver->submit(&t);
567 }
568 hobject_to_snap.erase(obj);
569 }
570
571 void check_oid() {
572 Mutex::Locker l(lock);
573 if (hobject_to_snap.empty())
574 return;
575 map<hobject_t, set<snapid_t>>::iterator obj =
576 rand_choose(hobject_to_snap);
577 set<snapid_t> snaps;
578 int r = mapper->get_snaps(obj->first, &snaps);
579 assert(r == 0);
580 ASSERT_EQ(snaps, obj->second);
581 }
582 };
583
584 class SnapMapperTest : public ::testing::Test {
585 protected:
586 boost::scoped_ptr< PausyAsyncMap > driver;
587 map<pg_t, ceph::shared_ptr<MapperVerifier> > mappers;
588 uint32_t pgnum;
589
590 void SetUp() override {
591 driver.reset(new PausyAsyncMap());
592 pgnum = 0;
593 }
594
595 void TearDown() override {
596 driver->stop();
597 mappers.clear();
598 driver.reset();
599 }
600
601 MapperVerifier &get_tester() {
602 //return *(mappers.begin()->second);
603 return *(rand_choose(mappers)->second);
604 }
605
606 void init(uint32_t to_set) {
607 pgnum = to_set;
608 for (uint32_t i = 0; i < pgnum; ++i) {
609 pg_t pgid(i, 0, -1);
610 mappers[pgid].reset(
611 new MapperVerifier(
612 driver.get(),
613 i,
614 pgid.get_split_bits(pgnum)
615 )
616 );
617 }
618 }
619
620 void run() {
621 for (int i = 0; i < 5000; ++i) {
622 if (!(i % 50))
623 std::cout << i << std::endl;
624 switch (rand() % 5) {
625 case 0:
626 get_tester().create_snap();
627 break;
628 case 1:
629 get_tester().create_object();
630 break;
631 case 2:
632 get_tester().trim_snap();
633 break;
634 case 3:
635 get_tester().check_oid();
636 break;
637 case 4:
638 get_tester().remove_oid();
639 break;
640 }
641 }
642 }
643 };
644
645 TEST_F(SnapMapperTest, Simple) {
646 init(1);
647 get_tester().create_snap();
648 get_tester().create_object();
649 get_tester().trim_snap();
650 }
651
652 TEST_F(SnapMapperTest, More) {
653 init(1);
654 run();
655 }
656
657 TEST_F(SnapMapperTest, MultiPG) {
658 init(50);
659 run();
660 }