]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/crimson/seastore/test_seastore.cc
update ceph source to reef 18.2.1
[ceph.git] / ceph / src / test / crimson / seastore / test_seastore.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include <string>
5 #include <iostream>
6 #include <sstream>
7
8 #include "test/crimson/gtest_seastar.h"
9
10 #include "test/crimson/seastore/transaction_manager_test_state.h"
11
12 #include "crimson/os/futurized_collection.h"
13 #include "crimson/os/seastore/seastore.h"
14 #include "crimson/os/seastore/onode.h"
15
16 using namespace crimson;
17 using namespace crimson::os;
18 using namespace crimson::os::seastore;
19 using SeaStoreShard = FuturizedStore::Shard;
20 using CTransaction = ceph::os::Transaction;
21 using namespace std;
22
23 namespace {
24 [[maybe_unused]] seastar::logger& logger() {
25 return crimson::get_logger(ceph_subsys_test);
26 }
27 }
28
29 ghobject_t make_oid(int i) {
30 stringstream ss;
31 ss << "object_" << i;
32 auto ret = ghobject_t(
33 hobject_t(
34 sobject_t(ss.str(), CEPH_NOSNAP)));
35 ret.set_shard(shard_id_t(shard_id_t::NO_SHARD));
36 ret.hobj.nspace = "asdf";
37 ret.hobj.pool = 0;
38 uint32_t reverse_hash = hobject_t::_reverse_bits(0);
39 ret.hobj.set_bitwise_key_u32(reverse_hash + i * 100);
40 return ret;
41 }
42
43 ghobject_t make_temp_oid(int i) {
44 stringstream ss;
45 ss << "temp_object_" << i;
46 auto ret = ghobject_t(
47 hobject_t(
48 sobject_t(ss.str(), CEPH_NOSNAP)));
49 ret.set_shard(shard_id_t(shard_id_t::NO_SHARD));
50 ret.hobj.nspace = "hjkl";
51 ret.hobj.pool = -2ll;
52 uint32_t reverse_hash = hobject_t::_reverse_bits(0);
53 ret.hobj.set_bitwise_key_u32(reverse_hash + i * 100);
54 return ret;
55 }
56
57 struct seastore_test_t :
58 public seastar_test_suite_t,
59 SeaStoreTestState {
60
61 coll_t coll_name{spg_t{pg_t{0, 0}}};
62 CollectionRef coll;
63
64 seastore_test_t() {}
65
66 seastar::future<> set_up_fut() final {
67 return tm_setup(
68 ).then([this] {
69 return sharded_seastore->create_new_collection(coll_name);
70 }).then([this](auto coll_ref) {
71 coll = coll_ref;
72 CTransaction t;
73 t.create_collection(coll_name, 0);
74 return sharded_seastore->do_transaction(
75 coll,
76 std::move(t));
77 });
78 }
79
80 seastar::future<> tear_down_fut() final {
81 coll.reset();
82 return tm_teardown();
83 }
84
85 void do_transaction(CTransaction &&t) {
86 return sharded_seastore->do_transaction(
87 coll,
88 std::move(t)).get0();
89 }
90
91 void set_meta(
92 const std::string& key,
93 const std::string& value) {
94 return seastore->write_meta(key, value).get0();
95 }
96
97 std::tuple<int, std::string> get_meta(
98 const std::string& key) {
99 return seastore->read_meta(key).get();
100 }
101
102 struct object_state_t {
103 const coll_t cid;
104 const CollectionRef coll;
105 const ghobject_t oid;
106
107 std::map<string, bufferlist> omap;
108 bufferlist contents;
109
110 std::map<snapid_t, bufferlist> clone_contents;
111
112 void touch(
113 CTransaction &t) {
114 t.touch(cid, oid);
115 }
116
117 void touch(
118 SeaStoreShard &sharded_seastore) {
119 CTransaction t;
120 touch(t);
121 sharded_seastore.do_transaction(
122 coll,
123 std::move(t)).get0();
124 }
125
126 void truncate(
127 CTransaction &t,
128 uint64_t off) {
129 t.truncate(cid, oid, off);
130 }
131
132 void truncate(
133 SeaStoreShard &sharded_seastore,
134 uint64_t off) {
135 CTransaction t;
136 truncate(t, off);
137 sharded_seastore.do_transaction(
138 coll,
139 std::move(t)).get0();
140 }
141
142 std::map<uint64_t, uint64_t> fiemap(
143 SeaStoreShard &sharded_seastore,
144 uint64_t off,
145 uint64_t len) {
146 return sharded_seastore.fiemap(coll, oid, off, len).unsafe_get0();
147 }
148
149 bufferlist readv(
150 SeaStoreShard &sharded_seastore,
151 interval_set<uint64_t>&m) {
152 return sharded_seastore.readv(coll, oid, m).unsafe_get0();
153 }
154
155 void remove(
156 CTransaction &t) {
157 t.remove(cid, oid);
158 t.remove_collection(cid);
159 }
160
161 void remove(
162 SeaStoreShard &sharded_seastore) {
163 CTransaction t;
164 remove(t);
165 sharded_seastore.do_transaction(
166 coll,
167 std::move(t)).get0();
168 }
169
170 void set_omap(
171 CTransaction &t,
172 const string &key,
173 const bufferlist &val) {
174 omap[key] = val;
175 std::map<string, bufferlist> arg;
176 arg[key] = val;
177 t.omap_setkeys(
178 cid,
179 oid,
180 arg);
181 }
182
183 void set_omap(
184 SeaStoreShard &sharded_seastore,
185 const string &key,
186 const bufferlist &val) {
187 CTransaction t;
188 set_omap(t, key, val);
189 sharded_seastore.do_transaction(
190 coll,
191 std::move(t)).get0();
192 }
193
194 void write(
195 SeaStoreShard &sharded_seastore,
196 CTransaction &t,
197 uint64_t offset,
198 bufferlist bl) {
199 bufferlist new_contents;
200 if (offset > 0 && contents.length()) {
201 new_contents.substr_of(
202 contents,
203 0,
204 std::min<size_t>(offset, contents.length())
205 );
206 }
207 new_contents.append_zero(offset - new_contents.length());
208 new_contents.append(bl);
209
210 auto tail_offset = offset + bl.length();
211 if (contents.length() > tail_offset) {
212 bufferlist tail;
213 tail.substr_of(
214 contents,
215 tail_offset,
216 contents.length() - tail_offset);
217 new_contents.append(tail);
218 }
219 contents.swap(new_contents);
220
221 t.write(
222 cid,
223 oid,
224 offset,
225 bl.length(),
226 bl);
227 }
228
229 void write(
230 SeaStoreShard &sharded_seastore,
231 uint64_t offset,
232 bufferlist bl) {
233 CTransaction t;
234 write(sharded_seastore, t, offset, bl);
235 sharded_seastore.do_transaction(
236 coll,
237 std::move(t)).get0();
238 }
239
240 void clone(
241 SeaStoreShard &sharded_seastore,
242 snapid_t snap) {
243 ghobject_t coid = oid;
244 coid.hobj.snap = snap;
245 CTransaction t;
246 t.clone(cid, oid, coid);
247 sharded_seastore.do_transaction(
248 coll,
249 std::move(t)).get0();
250 clone_contents[snap].reserve(contents.length());
251 auto it = contents.begin();
252 it.copy_all(clone_contents[snap]);
253 }
254
255 object_state_t get_clone(snapid_t snap) {
256 auto coid = oid;
257 coid.hobj.snap = snap;
258 auto clone_obj = object_state_t{cid, coll, coid};
259 clone_obj.contents.reserve(clone_contents[snap].length());
260 auto it = clone_contents[snap].begin();
261 it.copy_all(clone_obj.contents);
262 return clone_obj;
263 }
264
265 void write(
266 SeaStoreShard &sharded_seastore,
267 uint64_t offset,
268 size_t len,
269 char fill) {
270 auto buffer = bufferptr(buffer::create(len));
271 ::memset(buffer.c_str(), fill, len);
272 bufferlist bl;
273 bl.append(buffer);
274 write(sharded_seastore, offset, bl);
275 }
276
277 void zero(
278 SeaStoreShard &sharded_seastore,
279 CTransaction &t,
280 uint64_t offset,
281 size_t len) {
282 ceph::buffer::list bl;
283 bl.append_zero(len);
284 bufferlist new_contents;
285 if (offset > 0 && contents.length()) {
286 new_contents.substr_of(
287 contents,
288 0,
289 std::min<size_t>(offset, contents.length())
290 );
291 }
292 new_contents.append_zero(offset - new_contents.length());
293 new_contents.append(bl);
294
295 auto tail_offset = offset + bl.length();
296 if (contents.length() > tail_offset) {
297 bufferlist tail;
298 tail.substr_of(
299 contents,
300 tail_offset,
301 contents.length() - tail_offset);
302 new_contents.append(tail);
303 }
304 contents.swap(new_contents);
305
306 t.zero(
307 cid,
308 oid,
309 offset,
310 len);
311 }
312
313 void zero(
314 SeaStoreShard &sharded_seastore,
315 uint64_t offset,
316 size_t len) {
317 CTransaction t;
318 zero(sharded_seastore, t, offset, len);
319 sharded_seastore.do_transaction(
320 coll,
321 std::move(t)).get0();
322 }
323
324 void read(
325 SeaStoreShard &sharded_seastore,
326 uint64_t offset,
327 uint64_t len) {
328 bufferlist to_check;
329 if (contents.length() >= offset) {
330 to_check.substr_of(
331 contents,
332 offset,
333 std::min(len, (uint64_t)contents.length()));
334 }
335 auto ret = sharded_seastore.read(
336 coll,
337 oid,
338 offset,
339 len).unsafe_get0();
340 EXPECT_EQ(ret.length(), to_check.length());
341 EXPECT_EQ(ret, to_check);
342 }
343
344 void check_size(SeaStoreShard &sharded_seastore) {
345 auto st = sharded_seastore.stat(
346 coll,
347 oid).get0();
348 EXPECT_EQ(contents.length(), st.st_size);
349 }
350
351 void set_attr(
352 SeaStoreShard &sharded_seastore,
353 std::string key,
354 bufferlist& val) {
355 CTransaction t;
356 t.setattr(cid, oid, key, val);
357 sharded_seastore.do_transaction(
358 coll,
359 std::move(t)).get0();
360 }
361
362 void rm_attr(
363 SeaStoreShard &sharded_seastore,
364 std::string key) {
365 CTransaction t;
366 t.rmattr(cid, oid, key);
367 sharded_seastore.do_transaction(
368 coll,
369 std::move(t)).get0();
370 }
371
372 void rm_attrs(
373 SeaStoreShard &sharded_seastore) {
374 CTransaction t;
375 t.rmattrs(cid, oid);
376 sharded_seastore.do_transaction(
377 coll,
378 std::move(t)).get0();
379 }
380
381 SeaStoreShard::attrs_t get_attrs(
382 SeaStoreShard &sharded_seastore) {
383 return sharded_seastore.get_attrs(coll, oid)
384 .handle_error(SeaStoreShard::get_attrs_ertr::discard_all{})
385 .get();
386 }
387
388 ceph::bufferlist get_attr(
389 SeaStoreShard& sharded_seastore,
390 std::string_view name) {
391 return sharded_seastore.get_attr(coll, oid, name)
392 .handle_error(
393 SeaStoreShard::get_attr_errorator::discard_all{})
394 .get();
395 }
396
397 void check_omap_key(
398 SeaStoreShard &sharded_seastore,
399 const string &key) {
400 std::set<string> to_check;
401 to_check.insert(key);
402 auto result = sharded_seastore.omap_get_values(
403 coll,
404 oid,
405 to_check).unsafe_get0();
406 if (result.empty()) {
407 EXPECT_EQ(omap.find(key), omap.end());
408 } else {
409 auto iter = omap.find(key);
410 EXPECT_NE(iter, omap.end());
411 if (iter != omap.end()) {
412 EXPECT_EQ(result.size(), 1);
413 EXPECT_EQ(iter->second, result.begin()->second);
414 }
415 }
416 }
417
418 void check_omap(SeaStoreShard &sharded_seastore) {
419 auto refiter = omap.begin();
420 std::optional<std::string> start;
421 while(true) {
422 auto [done, kvs] = sharded_seastore.omap_get_values(
423 coll,
424 oid,
425 start).unsafe_get0();
426 auto iter = kvs.begin();
427 while (true) {
428 if ((done && iter == kvs.end()) && refiter == omap.end()) {
429 return; // finished
430 } else if (!done && iter == kvs.end()) {
431 break; // reload kvs
432 }
433 if (iter == kvs.end() || refiter->first < iter->first) {
434 logger().debug(
435 "check_omap: missing omap key {}",
436 refiter->first);
437 GTEST_FAIL() << "missing omap key " << refiter->first;
438 ++refiter;
439 } else if (refiter == omap.end() || refiter->first > iter->first) {
440 logger().debug(
441 "check_omap: extra omap key {}",
442 iter->first);
443 GTEST_FAIL() << "extra omap key " << iter->first;
444 ++iter;
445 } else {
446 EXPECT_EQ(iter->second, refiter->second);
447 ++iter;
448 ++refiter;
449 }
450 }
451 if (!done) {
452 start = kvs.rbegin()->first;
453 }
454 }
455 }
456 };
457
458 map<ghobject_t, object_state_t> test_objects;
459 object_state_t &get_object(
460 const ghobject_t &oid) {
461 return test_objects.emplace(
462 std::make_pair(
463 oid,
464 object_state_t{coll_name, coll, oid})).first->second;
465 }
466
467 void remove_object(
468 object_state_t &sobj) {
469
470 sobj.remove(*sharded_seastore);
471 auto erased = test_objects.erase(sobj.oid);
472 ceph_assert(erased == 1);
473 }
474
475 void validate_objects() const {
476 std::vector<ghobject_t> oids;
477 for (auto& [oid, obj] : test_objects) {
478 oids.emplace_back(oid);
479 }
480 auto ret = sharded_seastore->list_objects(
481 coll,
482 ghobject_t(),
483 ghobject_t::get_max(),
484 std::numeric_limits<uint64_t>::max()).get0();
485 EXPECT_EQ(std::get<1>(ret), ghobject_t::get_max());
486 EXPECT_EQ(std::get<0>(ret), oids);
487 }
488
489 // create temp objects
490 struct bound_t {
491 enum class type_t {
492 MIN,
493 MAX,
494 TEMP,
495 TEMP_END,
496 NORMAL_BEGIN,
497 NORMAL,
498 } type = type_t::MIN;
499 unsigned index = 0;
500
501 static bound_t get_temp(unsigned index) {
502 return bound_t{type_t::TEMP, index};
503 }
504 static bound_t get_normal(unsigned index) {
505 return bound_t{type_t::NORMAL, index};
506 }
507 static bound_t get_min() { return bound_t{type_t::MIN}; }
508 static bound_t get_max() { return bound_t{type_t::MAX}; }
509 static bound_t get_temp_end() { return bound_t{type_t::TEMP_END}; }
510 static bound_t get_normal_begin() {
511 return bound_t{type_t::NORMAL_BEGIN};
512 }
513
514 ghobject_t get_oid(SeaStore &seastore, CollectionRef &coll) const {
515 switch (type) {
516 case type_t::MIN:
517 return ghobject_t();
518 case type_t::MAX:
519 return ghobject_t::get_max();
520 case type_t::TEMP:
521 return make_temp_oid(index);
522 case type_t::TEMP_END:
523 return seastore.get_objs_range(coll, 0).temp_end;
524 case type_t::NORMAL_BEGIN:
525 return seastore.get_objs_range(coll, 0).obj_begin;
526 case type_t::NORMAL:
527 return make_oid(index);
528 default:
529 assert(0 == "impossible");
530 return ghobject_t();
531 }
532 }
533 };
534 struct list_test_case_t {
535 bound_t left;
536 bound_t right;
537 unsigned limit;
538 };
539 // list_test_cases_t :: [<limit, left_bound, right_bound>]
540 using list_test_cases_t = std::list<std::tuple<unsigned, bound_t, bound_t>>;
541
542 void test_list(
543 unsigned temp_to_create, /// create temp 0..temp_to_create-1
544 unsigned normal_to_create, /// create normal 0..normal_to_create-1
545 list_test_cases_t cases /// cases to test
546 ) {
547 std::vector<ghobject_t> objs;
548
549 // setup
550 auto create = [this, &objs](ghobject_t hoid) {
551 objs.emplace_back(std::move(hoid));
552 auto &obj = get_object(objs.back());
553 obj.touch(*sharded_seastore);
554 obj.check_size(*sharded_seastore);
555 };
556 for (unsigned i = 0; i < temp_to_create; ++i) {
557 create(make_temp_oid(i));
558 }
559 for (unsigned i = 0; i < normal_to_create; ++i) {
560 create(make_oid(i));
561 }
562
563 // list and validate each case
564 for (auto [limit, in_left_bound, in_right_bound] : cases) {
565 auto left_bound = in_left_bound.get_oid(*seastore, coll);
566 auto right_bound = in_right_bound.get_oid(*seastore, coll);
567
568 // get results from seastore
569 auto [listed, next] = sharded_seastore->list_objects(
570 coll, left_bound, right_bound, limit).get0();
571
572 // compute correct answer
573 auto correct_begin = std::find_if(
574 objs.begin(), objs.end(),
575 [&left_bound](const auto &in) {
576 return in >= left_bound;
577 });
578 unsigned count = 0;
579 auto correct_end = correct_begin;
580 for (; count < limit &&
581 correct_end != objs.end() &&
582 *correct_end < right_bound;
583 ++correct_end, ++count);
584
585 // validate return -- [correct_begin, correct_end) should match listed
586 decltype(objs) correct_listed(correct_begin, correct_end);
587 EXPECT_EQ(listed, correct_listed);
588
589 if (count < limit) {
590 if (correct_end == objs.end()) {
591 // if listed extends to end of range, next should be >= right_bound
592 EXPECT_GE(next, right_bound);
593 } else {
594 // next <= *correct_end since *correct_end is the next object to list
595 EXPECT_LE(next, *correct_end);
596 // next > *(correct_end - 1) since we already listed it
597 EXPECT_GT(next, *(correct_end - 1));
598 }
599 } else {
600 // we listed exactly limit objects
601 EXPECT_EQ(limit, listed.size());
602
603 EXPECT_GE(next, left_bound);
604 if (limit == 0) {
605 if (correct_end != objs.end()) {
606 // next <= *correct_end since *correct_end is the next object to list
607 EXPECT_LE(next, *correct_end);
608 }
609 } else {
610 // next > *(correct_end - 1) since we already listed it
611 EXPECT_GT(next, *(correct_end - 1));
612 }
613 }
614 }
615
616 // teardown
617 for (auto &&hoid : objs) { get_object(hoid).remove(*sharded_seastore); }
618 }
619 };
620
621 template <typename T, typename V>
622 auto contains(const T &t, const V &v) {
623 return std::find(
624 t.begin(),
625 t.end(),
626 v) != t.end();
627 }
628
629 TEST_P(seastore_test_t, collection_create_list_remove)
630 {
631 run_async([this] {
632 coll_t test_coll{spg_t{pg_t{1, 0}}};
633 {
634 sharded_seastore->create_new_collection(test_coll).get0();
635 {
636 CTransaction t;
637 t.create_collection(test_coll, 4);
638 do_transaction(std::move(t));
639 }
640 auto colls_cores = seastore->list_collections().get0();
641 std::vector<coll_t> colls;
642 colls.resize(colls_cores.size());
643 std::transform(
644 colls_cores.begin(), colls_cores.end(), colls.begin(),
645 [](auto p) { return p.first; });
646 EXPECT_EQ(colls.size(), 2);
647 EXPECT_TRUE(contains(colls, coll_name));
648 EXPECT_TRUE(contains(colls, test_coll));
649 }
650
651 {
652 {
653 CTransaction t;
654 t.remove_collection(test_coll);
655 do_transaction(std::move(t));
656 }
657 auto colls_cores = seastore->list_collections().get0();
658 std::vector<coll_t> colls;
659 colls.resize(colls_cores.size());
660 std::transform(
661 colls_cores.begin(), colls_cores.end(), colls.begin(),
662 [](auto p) { return p.first; });
663 EXPECT_EQ(colls.size(), 1);
664 EXPECT_TRUE(contains(colls, coll_name));
665 }
666 });
667 }
668
669 TEST_P(seastore_test_t, meta) {
670 run_async([this] {
671 set_meta("key1", "value1");
672 set_meta("key2", "value2");
673
674 const auto [ret1, value1] = get_meta("key1");
675 const auto [ret2, value2] = get_meta("key2");
676 EXPECT_EQ(ret1, 0);
677 EXPECT_EQ(ret2, 0);
678 EXPECT_EQ(value1, "value1");
679 EXPECT_EQ(value2, "value2");
680 });
681 }
682
683 TEST_P(seastore_test_t, touch_stat_list_remove)
684 {
685 run_async([this] {
686 auto &test_obj = get_object(make_oid(0));
687 test_obj.touch(*sharded_seastore);
688 test_obj.check_size(*sharded_seastore);
689 validate_objects();
690
691 remove_object(test_obj);
692 validate_objects();
693 });
694 }
695
696 using bound_t = seastore_test_t::bound_t;
697 constexpr unsigned MAX_LIMIT = std::numeric_limits<unsigned>::max();
698 static const seastore_test_t::list_test_cases_t temp_list_cases{
699 // list all temp, maybe overlap to normal on right
700 {MAX_LIMIT, bound_t::get_min() , bound_t::get_max() },
701 { 5, bound_t::get_min() , bound_t::get_temp_end()},
702 { 6, bound_t::get_min() , bound_t::get_temp_end()},
703 { 6, bound_t::get_min() , bound_t::get_max() },
704
705 // list temp starting at min up to but not past boundary
706 { 3, bound_t::get_min() , bound_t::get_temp(3) },
707 { 3, bound_t::get_min() , bound_t::get_temp(4) },
708 { 3, bound_t::get_min() , bound_t::get_temp(2) },
709
710 // list temp starting > min up to or past boundary
711 { 3, bound_t::get_temp(2) , bound_t::get_temp_end()},
712 { 3, bound_t::get_temp(2) , bound_t::get_max() },
713 { 3, bound_t::get_temp(3) , bound_t::get_max() },
714 { 3, bound_t::get_temp(1) , bound_t::get_max() },
715
716 // 0 limit
717 { 0, bound_t::get_min() , bound_t::get_max() },
718 { 0, bound_t::get_temp(1) , bound_t::get_max() },
719 { 0, bound_t::get_temp_end(), bound_t::get_max() },
720 };
721
722 TEST_P(seastore_test_t, list_objects_temp_only)
723 {
724 run_async([this] { test_list(5, 0, temp_list_cases); });
725 }
726
727 TEST_P(seastore_test_t, list_objects_temp_overlap)
728 {
729 run_async([this] { test_list(5, 5, temp_list_cases); });
730 }
731
732 static const seastore_test_t::list_test_cases_t normal_list_cases{
733 // list all normal, maybe overlap to temp on left
734 {MAX_LIMIT, bound_t::get_min() , bound_t::get_max() },
735 { 5, bound_t::get_normal_begin(), bound_t::get_max() },
736 { 6, bound_t::get_normal_begin(), bound_t::get_max() },
737 { 6, bound_t::get_temp(4) , bound_t::get_max() },
738
739 // list normal starting <= normal_begin < end
740 { 3, bound_t::get_normal_begin(), bound_t::get_normal(3)},
741 { 3, bound_t::get_normal_begin(), bound_t::get_normal(4)},
742 { 3, bound_t::get_normal_begin(), bound_t::get_normal(2)},
743 { 3, bound_t::get_temp(5) , bound_t::get_normal(2)},
744 { 3, bound_t::get_temp(4) , bound_t::get_normal(2)},
745
746 // list normal starting > min up to end
747 { 3, bound_t::get_normal(2) , bound_t::get_max() },
748 { 3, bound_t::get_normal(2) , bound_t::get_max() },
749 { 3, bound_t::get_normal(3) , bound_t::get_max() },
750 { 3, bound_t::get_normal(1) , bound_t::get_max() },
751
752 // 0 limit
753 { 0, bound_t::get_min() , bound_t::get_max() },
754 { 0, bound_t::get_normal(1) , bound_t::get_max() },
755 { 0, bound_t::get_normal_begin(), bound_t::get_max() },
756 };
757
758 TEST_P(seastore_test_t, list_objects_normal_only)
759 {
760 run_async([this] { test_list(5, 0, normal_list_cases); });
761 }
762
763 TEST_P(seastore_test_t, list_objects_normal_overlap)
764 {
765 run_async([this] { test_list(5, 5, normal_list_cases); });
766 }
767
768 bufferlist make_bufferlist(size_t len) {
769 bufferptr ptr(len);
770 bufferlist bl;
771 bl.append(ptr);
772 return bl;
773 }
774
775 TEST_P(seastore_test_t, omap_test_simple)
776 {
777 run_async([this] {
778 auto &test_obj = get_object(make_oid(0));
779 test_obj.touch(*sharded_seastore);
780 test_obj.set_omap(
781 *sharded_seastore,
782 "asdf",
783 make_bufferlist(128));
784 test_obj.check_omap_key(
785 *sharded_seastore,
786 "asdf");
787 });
788 }
789
790 TEST_P(seastore_test_t, clone_aligned_extents)
791 {
792 run_async([this] {
793 auto &test_obj = get_object(make_oid(0));
794 test_obj.write(*sharded_seastore, 0, 4096, 'a');
795
796 test_obj.clone(*sharded_seastore, 10);
797 std::cout << "reading origin after clone10" << std::endl;
798 test_obj.read(*sharded_seastore, 0, 4096);
799 test_obj.write(*sharded_seastore, 0, 4096, 'b');
800 test_obj.write(*sharded_seastore, 4096, 4096, 'c');
801 std::cout << "reading origin after clone10 and write" << std::endl;
802 test_obj.read(*sharded_seastore, 0, 8192);
803 auto clone_obj10 = test_obj.get_clone(10);
804 std::cout << "reading clone after clone10 and write" << std::endl;
805 clone_obj10.read(*sharded_seastore, 0, 8192);
806
807 test_obj.clone(*sharded_seastore, 20);
808 std::cout << "reading origin after clone20" << std::endl;
809 test_obj.read(*sharded_seastore, 0, 4096);
810 test_obj.write(*sharded_seastore, 0, 4096, 'd');
811 test_obj.write(*sharded_seastore, 4096, 4096, 'e');
812 test_obj.write(*sharded_seastore, 8192, 4096, 'f');
813 std::cout << "reading origin after clone20 and write" << std::endl;
814 test_obj.read(*sharded_seastore, 0, 12288);
815 auto clone_obj20 = test_obj.get_clone(20);
816 std::cout << "reading clone after clone20 and write" << std::endl;
817 clone_obj10.read(*sharded_seastore, 0, 12288);
818 clone_obj20.read(*sharded_seastore, 0, 12288);
819 });
820 }
821
822 TEST_P(seastore_test_t, clone_unaligned_extents)
823 {
824 run_async([this] {
825 auto &test_obj = get_object(make_oid(0));
826 test_obj.write(*sharded_seastore, 0, 8192, 'a');
827 test_obj.write(*sharded_seastore, 8192, 8192, 'b');
828 test_obj.write(*sharded_seastore, 16384, 8192, 'c');
829
830 test_obj.clone(*sharded_seastore, 10);
831 test_obj.write(*sharded_seastore, 4096, 12288, 'd');
832 std::cout << "reading origin after clone10 and write" << std::endl;
833 test_obj.read(*sharded_seastore, 0, 24576);
834
835 auto clone_obj10 = test_obj.get_clone(10);
836 std::cout << "reading clone after clone10 and write" << std::endl;
837 clone_obj10.read(*sharded_seastore, 0, 24576);
838
839 test_obj.clone(*sharded_seastore, 20);
840 test_obj.write(*sharded_seastore, 8192, 12288, 'e');
841 std::cout << "reading origin after clone20 and write" << std::endl;
842 test_obj.read(*sharded_seastore, 0, 24576);
843
844 auto clone_obj20 = test_obj.get_clone(20);
845 std::cout << "reading clone after clone20 and write" << std::endl;
846 clone_obj10.read(*sharded_seastore, 0, 24576);
847 clone_obj20.read(*sharded_seastore, 0, 24576);
848
849 test_obj.write(*sharded_seastore, 0, 24576, 'f');
850 test_obj.clone(*sharded_seastore, 30);
851 test_obj.write(*sharded_seastore, 8192, 4096, 'g');
852 std::cout << "reading origin after clone30 and write" << std::endl;
853 test_obj.read(*sharded_seastore, 0, 24576);
854
855 auto clone_obj30 = test_obj.get_clone(30);
856 std::cout << "reading clone after clone30 and write" << std::endl;
857 clone_obj10.read(*sharded_seastore, 0, 24576);
858 clone_obj20.read(*sharded_seastore, 0, 24576);
859 clone_obj30.read(*sharded_seastore, 0, 24576);
860 });
861 }
862
863 TEST_P(seastore_test_t, attr)
864 {
865 run_async([this] {
866 auto& test_obj = get_object(make_oid(0));
867 test_obj.touch(*sharded_seastore);
868 {
869 std::string oi("asdfasdfasdf");
870 bufferlist bl;
871 encode(oi, bl);
872 test_obj.set_attr(*sharded_seastore, OI_ATTR, bl);
873
874 std::string ss("fdsfdsfs");
875 bl.clear();
876 encode(ss, bl);
877 test_obj.set_attr(*sharded_seastore, SS_ATTR, bl);
878
879 std::string test_val("ssssssssssss");
880 bl.clear();
881 encode(test_val, bl);
882 test_obj.set_attr(*sharded_seastore, "test_key", bl);
883
884 auto attrs = test_obj.get_attrs(*sharded_seastore);
885 std::string oi2;
886 bufferlist bl2 = attrs[OI_ATTR];
887 decode(oi2, bl2);
888 bl2.clear();
889 bl2 = attrs[SS_ATTR];
890 std::string ss2;
891 decode(ss2, bl2);
892 std::string test_val2;
893 bl2.clear();
894 bl2 = attrs["test_key"];
895 decode(test_val2, bl2);
896 EXPECT_EQ(ss, ss2);
897 EXPECT_EQ(oi, oi2);
898 EXPECT_EQ(test_val, test_val2);
899
900 bl2.clear();
901 bl2 = test_obj.get_attr(*sharded_seastore, "test_key");
902 test_val2.clear();
903 decode(test_val2, bl2);
904 EXPECT_EQ(test_val, test_val2);
905 //test rm_attrs
906 test_obj.rm_attrs(*sharded_seastore);
907 attrs = test_obj.get_attrs(*sharded_seastore);
908 EXPECT_EQ(attrs.find(OI_ATTR), attrs.end());
909 EXPECT_EQ(attrs.find(SS_ATTR), attrs.end());
910 EXPECT_EQ(attrs.find("test_key"), attrs.end());
911
912 std::cout << "test_key passed" << std::endl;
913 //create OI_ATTR with len > onode_layout_t::MAX_OI_LENGTH, rm OI_ATTR
914 //create SS_ATTR with len > onode_layout_t::MAX_SS_LENGTH, rm SS_ATTR
915 char oi_array[onode_layout_t::MAX_OI_LENGTH + 1] = {'a'};
916 std::string oi_str(&oi_array[0], sizeof(oi_array));
917 bl.clear();
918 encode(oi_str, bl);
919 test_obj.set_attr(*sharded_seastore, OI_ATTR, bl);
920
921 char ss_array[onode_layout_t::MAX_SS_LENGTH + 1] = {'b'};
922 std::string ss_str(&ss_array[0], sizeof(ss_array));
923 bl.clear();
924 encode(ss_str, bl);
925 test_obj.set_attr(*sharded_seastore, SS_ATTR, bl);
926
927 attrs = test_obj.get_attrs(*sharded_seastore);
928 bl2.clear();
929 bl2 = attrs[OI_ATTR];
930 std::string oi_str2;
931 decode(oi_str2, bl2);
932 EXPECT_EQ(oi_str, oi_str2);
933
934 bl2.clear();
935 bl2 = attrs[SS_ATTR];
936 std::string ss_str2;
937 decode(ss_str2, bl2);
938 EXPECT_EQ(ss_str, ss_str2);
939
940 bl2.clear();
941 ss_str2.clear();
942 bl2 = test_obj.get_attr(*sharded_seastore, SS_ATTR);
943 decode(ss_str2, bl2);
944 EXPECT_EQ(ss_str, ss_str2);
945
946 bl2.clear();
947 oi_str2.clear();
948 bl2 = test_obj.get_attr(*sharded_seastore, OI_ATTR);
949 decode(oi_str2, bl2);
950 EXPECT_EQ(oi_str, oi_str2);
951
952 test_obj.rm_attr(*sharded_seastore, OI_ATTR);
953 test_obj.rm_attr(*sharded_seastore, SS_ATTR);
954
955 attrs = test_obj.get_attrs(*sharded_seastore);
956 EXPECT_EQ(attrs.find(OI_ATTR), attrs.end());
957 EXPECT_EQ(attrs.find(SS_ATTR), attrs.end());
958 }
959 {
960 //create OI_ATTR with len <= onode_layout_t::MAX_OI_LENGTH, rm OI_ATTR
961 //create SS_ATTR with len <= onode_layout_t::MAX_SS_LENGTH, rm SS_ATTR
962 std::string oi("asdfasdfasdf");
963 bufferlist bl;
964 encode(oi, bl);
965 test_obj.set_attr(*sharded_seastore, OI_ATTR, bl);
966
967 std::string ss("f");
968 bl.clear();
969 encode(ss, bl);
970 test_obj.set_attr(*sharded_seastore, SS_ATTR, bl);
971
972 std::string test_val("ssssssssssss");
973 bl.clear();
974 encode(test_val, bl);
975 test_obj.set_attr(*sharded_seastore, "test_key", bl);
976
977 auto attrs = test_obj.get_attrs(*sharded_seastore);
978 std::string oi2;
979 bufferlist bl2 = attrs[OI_ATTR];
980 decode(oi2, bl2);
981 bl2.clear();
982 bl2 = attrs[SS_ATTR];
983 std::string ss2;
984 decode(ss2, bl2);
985 std::string test_val2;
986 bl2.clear();
987 bl2 = attrs["test_key"];
988 decode(test_val2, bl2);
989 EXPECT_EQ(ss, ss2);
990 EXPECT_EQ(oi, oi2);
991 EXPECT_EQ(test_val, test_val2);
992
993 test_obj.rm_attr(*sharded_seastore, OI_ATTR);
994 test_obj.rm_attr(*sharded_seastore, SS_ATTR);
995 test_obj.rm_attr(*sharded_seastore, "test_key");
996
997 attrs = test_obj.get_attrs(*sharded_seastore);
998 EXPECT_EQ(attrs.find(OI_ATTR), attrs.end());
999 EXPECT_EQ(attrs.find(SS_ATTR), attrs.end());
1000 EXPECT_EQ(attrs.find("test_key"), attrs.end());
1001 }
1002 {
1003 // create OI_ATTR with len > onode_layout_t::MAX_OI_LENGTH, then
1004 // overwrite it with another OI_ATTR len of which < onode_layout_t::MAX_OI_LENGTH
1005 // create SS_ATTR with len > onode_layout_t::MAX_SS_LENGTH, then
1006 // overwrite it with another SS_ATTR len of which < onode_layout_t::MAX_SS_LENGTH
1007 char oi_array[onode_layout_t::MAX_OI_LENGTH + 1] = {'a'};
1008 std::string oi(&oi_array[0], sizeof(oi_array));
1009 bufferlist bl;
1010 encode(oi, bl);
1011 test_obj.set_attr(*sharded_seastore, OI_ATTR, bl);
1012
1013 oi = "asdfasdfasdf";
1014 bl.clear();
1015 encode(oi, bl);
1016 test_obj.set_attr(*sharded_seastore, OI_ATTR, bl);
1017
1018 char ss_array[onode_layout_t::MAX_SS_LENGTH + 1] = {'b'};
1019 std::string ss(&ss_array[0], sizeof(ss_array));
1020 bl.clear();
1021 encode(ss, bl);
1022 test_obj.set_attr(*sharded_seastore, SS_ATTR, bl);
1023
1024 ss = "f";
1025 bl.clear();
1026 encode(ss, bl);
1027 test_obj.set_attr(*sharded_seastore, SS_ATTR, bl);
1028
1029 auto attrs = test_obj.get_attrs(*sharded_seastore);
1030 std::string oi2, ss2;
1031 bufferlist bl2 = attrs[OI_ATTR];
1032 decode(oi2, bl2);
1033 bl2.clear();
1034 bl2 = attrs[SS_ATTR];
1035 decode(ss2, bl2);
1036 EXPECT_EQ(oi, oi2);
1037 EXPECT_EQ(ss, ss2);
1038 }
1039 });
1040 }
1041
1042 TEST_P(seastore_test_t, omap_test_iterator)
1043 {
1044 run_async([this] {
1045 auto make_key = [](unsigned i) {
1046 std::stringstream ss;
1047 ss << "key" << i;
1048 return ss.str();
1049 };
1050 auto &test_obj = get_object(make_oid(0));
1051 test_obj.touch(*sharded_seastore);
1052 for (unsigned i = 0; i < 20; ++i) {
1053 test_obj.set_omap(
1054 *sharded_seastore,
1055 make_key(i),
1056 make_bufferlist(128));
1057 }
1058 test_obj.check_omap(*sharded_seastore);
1059 });
1060 }
1061
1062 TEST_P(seastore_test_t, object_data_omap_remove)
1063 {
1064 run_async([this] {
1065 auto make_key = [](unsigned i) {
1066 std::stringstream ss;
1067 ss << "key" << i;
1068 return ss.str();
1069 };
1070 auto &test_obj = get_object(make_oid(0));
1071 test_obj.touch(*sharded_seastore);
1072 for (unsigned i = 0; i < 1024; ++i) {
1073 test_obj.set_omap(
1074 *sharded_seastore,
1075 make_key(i),
1076 make_bufferlist(128));
1077 }
1078 test_obj.check_omap(*sharded_seastore);
1079
1080 for (uint64_t i = 0; i < 16; i++) {
1081 test_obj.write(
1082 *sharded_seastore,
1083 4096 * i,
1084 4096,
1085 'a');
1086 }
1087 test_obj.remove(*sharded_seastore);
1088 });
1089 }
1090
1091
1092 TEST_P(seastore_test_t, simple_extent_test)
1093 {
1094 run_async([this] {
1095 auto &test_obj = get_object(make_oid(0));
1096 test_obj.write(
1097 *sharded_seastore,
1098 1024,
1099 1024,
1100 'a');
1101 test_obj.read(
1102 *sharded_seastore,
1103 1024,
1104 1024);
1105 test_obj.check_size(*sharded_seastore);
1106 });
1107 }
1108
1109 TEST_P(seastore_test_t, fiemap_empty)
1110 {
1111 run_async([this] {
1112 auto &test_obj = get_object(make_oid(0));
1113 test_obj.touch(*sharded_seastore);
1114 test_obj.truncate(*sharded_seastore, 100000);
1115
1116 std::map<uint64_t, uint64_t> m;
1117 m = test_obj.fiemap(*sharded_seastore, 0, 100000);
1118 EXPECT_TRUE(m.empty());
1119
1120 test_obj.remove(*sharded_seastore);
1121 });
1122 }
1123
1124 TEST_P(seastore_test_t, fiemap_holes)
1125 {
1126 run_async([this] {
1127 const uint64_t MAX_EXTENTS = 100;
1128
1129 // large enough to ensure that seastore will allocate each write seperately
1130 const uint64_t SKIP_STEP = 16 << 10;
1131 auto &test_obj = get_object(make_oid(0));
1132 bufferlist bl;
1133 bl.append("foo");
1134
1135 test_obj.touch(*sharded_seastore);
1136 for (uint64_t i = 0; i < MAX_EXTENTS; i++) {
1137 test_obj.write(*sharded_seastore, SKIP_STEP * i, bl);
1138 }
1139
1140 { // fiemap test from 0 to SKIP_STEP * (MAX_EXTENTS - 1) + 3
1141 auto m = test_obj.fiemap(
1142 *sharded_seastore, 0, SKIP_STEP * (MAX_EXTENTS - 1) + 3);
1143 ASSERT_EQ(m.size(), MAX_EXTENTS);
1144 for (uint64_t i = 0; i < MAX_EXTENTS; i++) {
1145 ASSERT_TRUE(m.count(SKIP_STEP * i));
1146 ASSERT_GE(m[SKIP_STEP * i], bl.length());
1147 }
1148 }
1149
1150 { // fiemap test from SKIP_STEP to SKIP_STEP * (MAX_EXTENTS - 2) + 3
1151 auto m = test_obj.fiemap(
1152 *sharded_seastore, SKIP_STEP, SKIP_STEP * (MAX_EXTENTS - 3) + 3);
1153 ASSERT_EQ(m.size(), MAX_EXTENTS - 2);
1154 for (uint64_t i = 1; i < MAX_EXTENTS - 1; i++) {
1155 ASSERT_TRUE(m.count(SKIP_STEP * i));
1156 ASSERT_GE(m[SKIP_STEP * i], bl.length());
1157 }
1158 }
1159
1160 { // fiemap test SKIP_STEP + 1 to 2 * SKIP_STEP + 1 (partial overlap)
1161 auto m = test_obj.fiemap(
1162 *sharded_seastore, SKIP_STEP + 1, SKIP_STEP + 1);
1163 ASSERT_EQ(m.size(), 2);
1164 ASSERT_EQ(m.begin()->first, SKIP_STEP + 1);
1165 ASSERT_GE(m.begin()->second, bl.length());
1166 ASSERT_LE(m.rbegin()->first, (2 * SKIP_STEP) + 1);
1167 ASSERT_EQ(m.rbegin()->first + m.rbegin()->second, 2 * SKIP_STEP + 2);
1168 }
1169
1170 test_obj.remove(*sharded_seastore);
1171 });
1172 }
1173
1174 TEST_P(seastore_test_t, sparse_read)
1175 {
1176 run_async([this] {
1177 const uint64_t MAX_EXTENTS = 100;
1178 const uint64_t SKIP_STEP = 16 << 10;
1179 auto &test_obj = get_object(make_oid(0));
1180 bufferlist wbl;
1181 wbl.append("foo");
1182
1183 test_obj.touch(*sharded_seastore);
1184 for (uint64_t i = 0; i < MAX_EXTENTS; i++) {
1185 test_obj.write(*sharded_seastore, SKIP_STEP * i, wbl);
1186 }
1187 interval_set<uint64_t> m;
1188 m = interval_set<uint64_t>(
1189 test_obj.fiemap(*sharded_seastore, 0, SKIP_STEP * (MAX_EXTENTS - 1) + 3));
1190 ASSERT_TRUE(!m.empty());
1191 uint64_t off = 0;
1192 auto rbl = test_obj.readv(*sharded_seastore, m);
1193
1194 for (auto &&miter : m) {
1195 bufferlist subl;
1196 subl.substr_of(rbl, off, std::min(miter.second, uint64_t(wbl.length())));
1197 ASSERT_TRUE(subl.contents_equal(wbl));
1198 off += miter.second;
1199 }
1200 test_obj.remove(*sharded_seastore);
1201 });
1202 }
1203
1204 TEST_P(seastore_test_t, zero)
1205 {
1206 run_async([this] {
1207 auto test_zero = [this](
1208 // [(off, len, repeat)]
1209 std::vector<std::tuple<uint64_t, uint64_t, uint64_t>> writes,
1210 uint64_t zero_off, uint64_t zero_len) {
1211
1212 // Test zero within a block
1213 auto &test_obj = get_object(make_oid(0));
1214 uint64_t size = 0;
1215 for (auto &[off, len, repeat]: writes) {
1216 for (decltype(repeat) i = 0; i < repeat; ++i) {
1217 test_obj.write(*sharded_seastore, off + (len * repeat), len, 'a');
1218 }
1219 size = off + (len * (repeat + 1));
1220 }
1221 test_obj.read(
1222 *sharded_seastore,
1223 0,
1224 size);
1225 test_obj.check_size(*sharded_seastore);
1226 test_obj.zero(*sharded_seastore, zero_off, zero_len);
1227 test_obj.read(
1228 *sharded_seastore,
1229 0,
1230 size);
1231 test_obj.check_size(*sharded_seastore);
1232 remove_object(test_obj);
1233 };
1234
1235 const uint64_t BS = 4<<10;
1236
1237 // Test zero within a block
1238 test_zero(
1239 {{1<<10, 1<<10, 1}},
1240 1124, 200);
1241
1242 // Multiple writes, partial on left, partial on right.
1243 test_zero(
1244 {{BS, BS, 10}},
1245 BS + 128,
1246 BS * 4);
1247
1248 // Single large write, block boundary on right, partial on left.
1249 test_zero(
1250 {{BS, BS * 10, 1}},
1251 BS + 128,
1252 (BS * 4) - 128);
1253
1254 // Multiple writes, block boundary on left, partial on right.
1255 test_zero(
1256 {{BS, BS, 10}},
1257 BS,
1258 (BS * 4) + 128);
1259 });
1260 }
1261 INSTANTIATE_TEST_SUITE_P(
1262 seastore_test,
1263 seastore_test_t,
1264 ::testing::Values (
1265 "segmented",
1266 "circularbounded"
1267 )
1268 );