]>
git.proxmox.com Git - ceph.git/blob - ceph/src/test/test_snap_mapper.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
5 #include <boost/scoped_ptr.hpp>
9 #include "include/buffer.h"
10 #include "common/map_cacher.hpp"
11 #include "osd/SnapMapper.h"
12 #include "common/Cond.h"
14 #include "gtest/gtest.h"
19 typename
T::iterator
rand_choose(T
&cont
) {
20 if (std::empty(cont
)) {
21 return std::end(cont
);
23 return std::next(std::begin(cont
), rand() % cont
.size());
26 string
random_string(size_t size
)
29 for (size_t j
= 0; j
< size
; ++j
) {
30 name
.push_back('a' + (rand() % 26));
35 class PausyAsyncMap
: public MapCacher::StoreDriver
<string
, bufferlist
> {
37 virtual void operate(map
<string
, bufferlist
> *store
) = 0;
40 typedef std::shared_ptr
<_Op
> Op
;
41 struct Remove
: public _Op
{
42 set
<string
> to_remove
;
43 explicit Remove(const set
<string
> &to_remove
) : to_remove(to_remove
) {}
44 void operate(map
<string
, bufferlist
> *store
) override
{
45 for (set
<string
>::iterator i
= to_remove
.begin();
52 struct Insert
: public _Op
{
53 map
<string
, bufferlist
> to_insert
;
54 explicit Insert(const map
<string
, bufferlist
> &to_insert
) : to_insert(to_insert
) {}
55 void operate(map
<string
, bufferlist
> *store
) override
{
56 for (map
<string
, bufferlist
>::iterator i
= to_insert
.begin();
59 store
->erase(i
->first
);
64 struct Callback
: public _Op
{
66 explicit Callback(Context
*c
) : context(c
) {}
67 void operate(map
<string
, bufferlist
> *store
) override
{
72 class Transaction
: public MapCacher::Transaction
<string
, bufferlist
> {
73 friend class PausyAsyncMap
;
77 void set_keys(const map
<string
, bufferlist
> &i
) override
{
78 ops
.push_back(Op(new Insert(i
)));
80 void remove_keys(const set
<string
> &r
) override
{
81 ops
.push_back(Op(new Remove(r
)));
83 void add_callback(Context
*c
) override
{
84 callbacks
.push_back(Op(new Callback(c
)));
89 ceph::mutex lock
= ceph::make_mutex("PausyAsyncMap");
90 map
<string
, bufferlist
> store
;
92 class Doer
: public Thread
{
93 static const size_t MAX_SIZE
= 100;
94 PausyAsyncMap
*parent
;
95 ceph::mutex lock
= ceph::make_mutex("Doer lock");
96 ceph::condition_variable cond
;
101 explicit Doer(PausyAsyncMap
*parent
) :
102 parent(parent
), stopping(0), paused(false) {}
103 void *entry() override
{
107 std::unique_lock l
{lock
};
108 cond
.wait(l
, [this] {
109 return stopping
|| (!queue
.empty() && !paused
);
111 if (stopping
&& queue
.empty()) {
116 ceph_assert(!queue
.empty());
117 ceph_assert(!paused
);
121 ceph_assert(!ops
.empty());
123 for (list
<Op
>::iterator i
= ops
.begin();
127 usleep(1+(rand() % 5000));
128 std::lock_guard l
{parent
->lock
};
129 (*i
)->operate(&(parent
->store
));
135 std::lock_guard l
{lock
};
141 std::lock_guard l
{lock
};
146 void submit(list
<Op
> &in
) {
147 std::unique_lock l
{lock
};
148 cond
.wait(l
, [this] { return queue
.size() < MAX_SIZE
;});
149 queue
.splice(queue
.end(), in
, in
.begin(), in
.end());
154 std::unique_lock l
{lock
};
157 cond
.wait(l
, [this] { return stopping
== 2; });
163 PausyAsyncMap() : doer(this) {
166 ~PausyAsyncMap() override
{
170 const set
<string
> &keys
,
171 map
<string
, bufferlist
> *out
) override
{
172 std::lock_guard l
{lock
};
173 for (set
<string
>::const_iterator i
= keys
.begin();
176 map
<string
, bufferlist
>::iterator j
= store
.find(*i
);
177 if (j
!= store
.end())
184 pair
<string
, bufferlist
> *next
) override
{
185 std::lock_guard l
{lock
};
186 map
<string
, bufferlist
>::iterator j
= store
.upper_bound(key
);
187 if (j
!= store
.end()) {
195 void submit(Transaction
*t
) {
197 doer
.submit(t
->callbacks
);
201 ceph::mutex lock
= ceph::make_mutex("flush lock");
202 ceph::condition_variable cond
;
205 class OnFinish
: public Context
{
207 ceph::condition_variable
*cond
;
210 OnFinish(ceph::mutex
*lock
, ceph::condition_variable
*cond
, bool *done
)
211 : lock(lock
), cond(cond
), done(done
) {}
212 void finish(int) override
{
213 std::lock_guard l
{*lock
};
219 t
.add_callback(new OnFinish(&lock
, &cond
, &done
));
222 std::unique_lock l
{lock
};
223 cond
.wait(l
, [&] { return done
; });
239 class MapCacherTest
: public ::testing::Test
{
241 boost::scoped_ptr
< PausyAsyncMap
> driver
;
242 boost::scoped_ptr
<MapCacher::MapCacher
<string
, bufferlist
> > cache
;
243 map
<string
, bufferlist
> truth
;
246 void assert_bl_eq(bufferlist
&bl1
, bufferlist
&bl2
) {
247 ASSERT_EQ(bl1
.length(), bl2
.length());
248 bufferlist::iterator j
= bl2
.begin();
249 for (bufferlist::iterator i
= bl1
.begin();
252 ASSERT_TRUE(!j
.end());
256 void assert_bl_map_eq(map
<string
, bufferlist
> &m1
,
257 map
<string
, bufferlist
> &m2
) {
258 ASSERT_EQ(m1
.size(), m2
.size());
259 map
<string
, bufferlist
>::iterator j
= m2
.begin();
260 for (map
<string
, bufferlist
>::iterator i
= m1
.begin();
263 ASSERT_TRUE(j
!= m2
.end());
264 ASSERT_EQ(i
->first
, j
->first
);
265 assert_bl_eq(i
->second
, j
->second
);
269 size_t random_num() {
270 return random() % 10;
272 size_t random_size() {
273 return random() % 1000;
275 void random_bl(size_t size
, bufferlist
*bl
) {
276 for (size_t i
= 0; i
< size
; ++i
) {
281 size_t set_size
= random_num();
282 map
<string
, bufferlist
> to_set
;
283 for (size_t i
= 0; i
< set_size
; ++i
) {
285 random_bl(random_size(), &bl
);
286 string key
= *rand_choose(names
);
290 for (map
<string
, bufferlist
>::iterator i
= to_set
.begin();
293 truth
.erase(i
->first
);
297 PausyAsyncMap::Transaction t
;
298 cache
->set_keys(to_set
, &t
);
303 size_t remove_size
= random_num();
304 set
<string
> to_remove
;
305 for (size_t i
= 0; i
< remove_size
; ++i
) {
306 to_remove
.insert(*rand_choose(names
));
308 for (set
<string
>::iterator i
= to_remove
.begin();
309 i
!= to_remove
.end();
314 PausyAsyncMap::Transaction t
;
315 cache
->remove_keys(to_remove
, &t
);
321 size_t get_size
= random_num();
322 for (size_t i
= 0; i
< get_size
; ++i
) {
323 to_get
.insert(*rand_choose(names
));
326 map
<string
, bufferlist
> got_truth
;
327 for (set
<string
>::iterator i
= to_get
.begin();
330 map
<string
, bufferlist
>::iterator j
= truth
.find(*i
);
331 if (j
!= truth
.end())
332 got_truth
.insert(*j
);
335 map
<string
, bufferlist
> got
;
336 cache
->get_keys(to_get
, &got
);
338 assert_bl_map_eq(got
, got_truth
);
344 pair
<string
, bufferlist
> next
;
345 int r
= cache
->get_next(cur
, &next
);
347 pair
<string
, bufferlist
> next_truth
;
348 map
<string
, bufferlist
>::iterator i
= truth
.upper_bound(cur
);
349 int r_truth
= (i
== truth
.end()) ? -ENOENT
: 0;
350 if (i
!= truth
.end())
353 ASSERT_EQ(r
, r_truth
);
357 ASSERT_EQ(next
.first
, next_truth
.first
);
358 assert_bl_eq(next
.second
, next_truth
.second
);
362 void SetUp() override
{
363 driver
.reset(new PausyAsyncMap());
364 cache
.reset(new MapCacher::MapCacher
<string
, bufferlist
>(driver
.get()));
367 size_t names_size(random_num() + 10);
368 for (size_t i
= 0; i
< names_size
; ++i
) {
369 names
.insert(random_string(1 + (random_size() % 10)));
372 void TearDown() override
{
380 TEST_F(MapCacherTest
, Simple
)
383 map
<string
, bufferlist
> truth
;
384 set
<string
> truth_keys
;
388 truth
[string("asdf")] = bl
;
389 truth_keys
.insert(truth
.begin()->first
);
391 PausyAsyncMap::Transaction t
;
392 cache
->set_keys(truth
, &t
);
394 cache
->set_keys(truth
, &t
);
398 map
<string
, bufferlist
> got
;
399 cache
->get_keys(truth_keys
, &got
);
400 assert_bl_map_eq(got
, truth
);
406 cache
->get_keys(truth_keys
, &got
);
407 assert_bl_map_eq(got
, truth
);
410 TEST_F(MapCacherTest
, Random
)
412 for (size_t i
= 0; i
< 5000; ++i
) {
414 std::cout
<< "On iteration " << i
<< std::endl
;
416 switch (rand() % 4) {
433 class MapperVerifier
{
434 PausyAsyncMap
*driver
;
435 boost::scoped_ptr
< SnapMapper
> mapper
;
436 map
<snapid_t
, set
<hobject_t
> > snap_to_hobject
;
437 map
<hobject_t
, set
<snapid_t
>> hobject_to_snap
;
441 ceph::mutex lock
= ceph::make_mutex("lock");
445 PausyAsyncMap
*driver
,
449 mapper(new SnapMapper(g_ceph_context
, driver
, mask
, bits
, 0, shard_id_t(1))),
450 mask(mask
), bits(bits
) {}
452 hobject_t
random_hobject() {
454 random_string(1+(rand() % 16)),
455 random_string(1+(rand() % 16)),
456 snapid_t(rand() % 1000),
457 (rand() & ((~0)<<bits
)) | (mask
& ~((~0)<<bits
)),
458 0, random_string(rand() % 16));
461 void choose_random_snaps(int num
, set
<snapid_t
> *snaps
) {
463 ceph_assert(!snap_to_hobject
.empty());
464 for (int i
= 0; i
< num
|| snaps
->empty(); ++i
) {
465 snaps
->insert(rand_choose(snap_to_hobject
)->first
);
470 snap_to_hobject
[next
];
474 void create_object() {
475 std::lock_guard l
{lock
};
476 if (snap_to_hobject
.empty())
480 obj
= random_hobject();
481 } while (hobject_to_snap
.count(obj
));
483 set
<snapid_t
> &snaps
= hobject_to_snap
[obj
];
484 choose_random_snaps(1 + (rand() % 20), &snaps
);
485 for (set
<snapid_t
>::iterator i
= snaps
.begin();
488 map
<snapid_t
, set
<hobject_t
> >::iterator j
= snap_to_hobject
.find(*i
);
489 ceph_assert(j
!= snap_to_hobject
.end());
490 j
->second
.insert(obj
);
493 PausyAsyncMap::Transaction t
;
494 mapper
->add_oid(obj
, snaps
, &t
);
499 std::pair
<std::string
, ceph::buffer::list
> to_raw(
500 const std::pair
<snapid_t
, hobject_t
> &to_map
) {
501 return mapper
->to_raw(to_map
);
504 std::string
to_legacy_raw_key(
505 const std::pair
<snapid_t
, hobject_t
> &to_map
) {
506 return mapper
->to_legacy_raw_key(to_map
);
509 std::string
to_raw_key(
510 const std::pair
<snapid_t
, hobject_t
> &to_map
) {
511 return mapper
->to_raw_key(to_map
);
515 std::lock_guard l
{lock
};
516 if (snap_to_hobject
.empty())
518 map
<snapid_t
, set
<hobject_t
> >::iterator snap
=
519 rand_choose(snap_to_hobject
);
520 set
<hobject_t
> hobjects
= snap
->second
;
522 vector
<hobject_t
> hoids
;
523 while (mapper
->get_next_objects_to_trim(
524 snap
->first
, rand() % 5 + 1, &hoids
) == 0) {
525 for (auto &&hoid
: hoids
) {
526 ceph_assert(!hoid
.is_max());
527 ceph_assert(hobjects
.count(hoid
));
528 hobjects
.erase(hoid
);
530 map
<hobject_t
, set
<snapid_t
>>::iterator j
=
531 hobject_to_snap
.find(hoid
);
532 ceph_assert(j
->second
.count(snap
->first
));
533 set
<snapid_t
> old_snaps(j
->second
);
534 j
->second
.erase(snap
->first
);
537 PausyAsyncMap::Transaction t
;
538 mapper
->update_snaps(
545 if (j
->second
.empty()) {
546 hobject_to_snap
.erase(j
);
548 hoid
= hobject_t::get_max();
552 ceph_assert(hobjects
.empty());
553 snap_to_hobject
.erase(snap
);
557 std::lock_guard l
{lock
};
558 if (hobject_to_snap
.empty())
560 map
<hobject_t
, set
<snapid_t
>>::iterator obj
=
561 rand_choose(hobject_to_snap
);
562 for (set
<snapid_t
>::iterator i
= obj
->second
.begin();
563 i
!= obj
->second
.end();
565 map
<snapid_t
, set
<hobject_t
> >::iterator j
=
566 snap_to_hobject
.find(*i
);
567 ceph_assert(j
->second
.count(obj
->first
));
568 j
->second
.erase(obj
->first
);
571 PausyAsyncMap::Transaction t
;
577 hobject_to_snap
.erase(obj
);
581 std::lock_guard l
{lock
};
582 if (hobject_to_snap
.empty())
584 map
<hobject_t
, set
<snapid_t
>>::iterator obj
=
585 rand_choose(hobject_to_snap
);
587 int r
= mapper
->get_snaps(obj
->first
, &snaps
);
589 ASSERT_EQ(snaps
, obj
->second
);
593 class SnapMapperTest
: public ::testing::Test
{
595 boost::scoped_ptr
< PausyAsyncMap
> driver
;
596 map
<pg_t
, std::shared_ptr
<MapperVerifier
> > mappers
;
599 void SetUp() override
{
600 driver
.reset(new PausyAsyncMap());
604 void TearDown() override
{
610 MapperVerifier
&get_tester() {
611 //return *(mappers.begin()->second);
612 return *(rand_choose(mappers
)->second
);
615 void init(uint32_t to_set
) {
617 for (uint32_t i
= 0; i
< pgnum
; ++i
) {
623 pgid
.get_split_bits(pgnum
)
630 for (int i
= 0; i
< 5000; ++i
) {
632 std::cout
<< i
<< std::endl
;
633 switch (rand() % 5) {
635 get_tester().create_snap();
638 get_tester().create_object();
641 get_tester().trim_snap();
644 get_tester().check_oid();
647 get_tester().remove_oid();
654 TEST_F(SnapMapperTest
, Simple
) {
656 get_tester().create_snap();
657 get_tester().create_object();
658 get_tester().trim_snap();
661 TEST_F(SnapMapperTest
, More
) {
666 TEST_F(SnapMapperTest
, MultiPG
) {
671 TEST_F(SnapMapperTest
, LegacyKeyConvertion
) {
673 auto obj
= get_tester().random_hobject();
674 snapid_t snapid
= random() % 10;
675 auto snap_obj
= make_pair(snapid
, obj
);
676 auto raw
= get_tester().to_raw(snap_obj
);
677 std::string old_key
= get_tester().to_legacy_raw_key(snap_obj
);
678 std::string converted_key
=
679 SnapMapper::convert_legacy_key(old_key
, raw
.second
);
680 std::string new_key
= get_tester().to_raw_key(snap_obj
);
681 std::cout
<< "Converted: " << old_key
<< "\nTo: " << converted_key
682 << "\nNew key: " << new_key
<< std::endl
;
683 ASSERT_EQ(converted_key
, new_key
);