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