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