1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
8 #include "test/crimson/gtest_seastar.h"
10 #include "test/crimson/seastore/transaction_manager_test_state.h"
12 #include "crimson/os/futurized_collection.h"
13 #include "crimson/os/seastore/seastore.h"
14 #include "crimson/os/seastore/onode.h"
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
;
24 [[maybe_unused
]] seastar::logger
& logger() {
25 return crimson::get_logger(ceph_subsys_test
);
29 ghobject_t
make_oid(int i
) {
32 auto ret
= ghobject_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";
38 uint32_t reverse_hash
= hobject_t::_reverse_bits(0);
39 ret
.hobj
.set_bitwise_key_u32(reverse_hash
+ i
* 100);
43 ghobject_t
make_temp_oid(int i
) {
45 ss
<< "temp_object_" << i
;
46 auto ret
= ghobject_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";
52 uint32_t reverse_hash
= hobject_t::_reverse_bits(0);
53 ret
.hobj
.set_bitwise_key_u32(reverse_hash
+ i
* 100);
57 struct seastore_test_t
:
58 public seastar_test_suite_t
,
60 ::testing::WithParamInterface
<const char*> {
62 coll_t coll_name
{spg_t
{pg_t
{0, 0}}};
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
;
75 ceph_assert(0 == "no support");
77 return tm_setup(journal
79 return sharded_seastore
->create_new_collection(coll_name
);
80 }).then([this](auto coll_ref
) {
83 t
.create_collection(coll_name
, 0);
84 return sharded_seastore
->do_transaction(
90 seastar::future
<> tear_down_fut() final
{
95 void do_transaction(CTransaction
&&t
) {
96 return sharded_seastore
->do_transaction(
102 const std::string
& key
,
103 const std::string
& value
) {
104 return seastore
->write_meta(key
, value
).get0();
107 std::tuple
<int, std::string
> get_meta(
108 const std::string
& key
) {
109 return seastore
->read_meta(key
).get();
112 struct object_state_t
{
114 const CollectionRef coll
;
115 const ghobject_t oid
;
117 std::map
<string
, bufferlist
> omap
;
126 SeaStoreShard
&sharded_seastore
) {
129 sharded_seastore
.do_transaction(
131 std::move(t
)).get0();
137 t
.truncate(cid
, oid
, off
);
141 SeaStoreShard
&sharded_seastore
,
145 sharded_seastore
.do_transaction(
147 std::move(t
)).get0();
150 std::map
<uint64_t, uint64_t> fiemap(
151 SeaStoreShard
&sharded_seastore
,
154 return sharded_seastore
.fiemap(coll
, oid
, off
, len
).unsafe_get0();
158 SeaStoreShard
&sharded_seastore
,
159 interval_set
<uint64_t>&m
) {
160 return sharded_seastore
.readv(coll
, oid
, m
).unsafe_get0();
166 t
.remove_collection(cid
);
170 SeaStoreShard
&sharded_seastore
) {
173 sharded_seastore
.do_transaction(
175 std::move(t
)).get0();
181 const bufferlist
&val
) {
183 std::map
<string
, bufferlist
> arg
;
192 SeaStoreShard
&sharded_seastore
,
194 const bufferlist
&val
) {
196 set_omap(t
, key
, val
);
197 sharded_seastore
.do_transaction(
199 std::move(t
)).get0();
203 SeaStoreShard
&sharded_seastore
,
207 bufferlist new_contents
;
208 if (offset
> 0 && contents
.length()) {
209 new_contents
.substr_of(
212 std::min
<size_t>(offset
, contents
.length())
215 new_contents
.append_zero(offset
- new_contents
.length());
216 new_contents
.append(bl
);
218 auto tail_offset
= offset
+ bl
.length();
219 if (contents
.length() > tail_offset
) {
224 contents
.length() - tail_offset
);
225 new_contents
.append(tail
);
227 contents
.swap(new_contents
);
238 SeaStoreShard
&sharded_seastore
,
242 write(sharded_seastore
, t
, offset
, bl
);
243 sharded_seastore
.do_transaction(
245 std::move(t
)).get0();
248 SeaStoreShard
&sharded_seastore
,
252 auto buffer
= bufferptr(buffer::create(len
));
253 ::memset(buffer
.c_str(), fill
, len
);
256 write(sharded_seastore
, offset
, bl
);
260 SeaStoreShard
&sharded_seastore
,
264 ceph::buffer::list bl
;
266 bufferlist new_contents
;
267 if (offset
> 0 && contents
.length()) {
268 new_contents
.substr_of(
271 std::min
<size_t>(offset
, contents
.length())
274 new_contents
.append_zero(offset
- new_contents
.length());
275 new_contents
.append(bl
);
277 auto tail_offset
= offset
+ bl
.length();
278 if (contents
.length() > tail_offset
) {
283 contents
.length() - tail_offset
);
284 new_contents
.append(tail
);
286 contents
.swap(new_contents
);
296 SeaStoreShard
&sharded_seastore
,
300 zero(sharded_seastore
, t
, offset
, len
);
301 sharded_seastore
.do_transaction(
303 std::move(t
)).get0();
307 SeaStoreShard
&sharded_seastore
,
315 auto ret
= sharded_seastore
.read(
320 EXPECT_EQ(ret
.length(), to_check
.length());
321 EXPECT_EQ(ret
, to_check
);
324 void check_size(SeaStoreShard
&sharded_seastore
) {
325 auto st
= sharded_seastore
.stat(
328 EXPECT_EQ(contents
.length(), st
.st_size
);
332 SeaStoreShard
&sharded_seastore
,
336 t
.setattr(cid
, oid
, key
, val
);
337 sharded_seastore
.do_transaction(
339 std::move(t
)).get0();
343 SeaStoreShard
&sharded_seastore
,
346 t
.rmattr(cid
, oid
, key
);
347 sharded_seastore
.do_transaction(
349 std::move(t
)).get0();
353 SeaStoreShard
&sharded_seastore
) {
356 sharded_seastore
.do_transaction(
358 std::move(t
)).get0();
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
{})
368 ceph::bufferlist
get_attr(
369 SeaStoreShard
& sharded_seastore
,
370 std::string_view name
) {
371 return sharded_seastore
.get_attr(coll
, oid
, name
)
373 SeaStoreShard::get_attr_errorator::discard_all
{})
378 SeaStoreShard
&sharded_seastore
,
380 std::set
<string
> to_check
;
381 to_check
.insert(key
);
382 auto result
= sharded_seastore
.omap_get_values(
385 to_check
).unsafe_get0();
386 if (result
.empty()) {
387 EXPECT_EQ(omap
.find(key
), omap
.end());
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
);
398 void check_omap(SeaStoreShard
&sharded_seastore
) {
399 auto refiter
= omap
.begin();
400 std::optional
<std::string
> start
;
402 auto [done
, kvs
] = sharded_seastore
.omap_get_values(
405 start
).unsafe_get0();
406 auto iter
= kvs
.begin();
408 if ((done
&& iter
== kvs
.end()) && refiter
== omap
.end()) {
410 } else if (!done
&& iter
== kvs
.end()) {
413 if (iter
== kvs
.end() || refiter
->first
< iter
->first
) {
415 "check_omap: missing omap key {}",
417 GTEST_FAIL() << "missing omap key " << refiter
->first
;
419 } else if (refiter
== omap
.end() || refiter
->first
> iter
->first
) {
421 "check_omap: extra omap key {}",
423 GTEST_FAIL() << "extra omap key " << iter
->first
;
426 EXPECT_EQ(iter
->second
, refiter
->second
);
432 start
= kvs
.rbegin()->first
;
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(
444 object_state_t
{coll_name
, coll
, oid
})).first
->second
;
448 object_state_t
&sobj
) {
450 sobj
.remove(*sharded_seastore
);
451 auto erased
= test_objects
.erase(sobj
.oid
);
452 ceph_assert(erased
== 1);
455 void validate_objects() const {
456 std::vector
<ghobject_t
> oids
;
457 for (auto& [oid
, obj
] : test_objects
) {
458 oids
.emplace_back(oid
);
460 auto ret
= sharded_seastore
->list_objects(
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
);
469 // create temp objects
478 } type
= type_t::MIN
;
481 static bound_t
get_temp(unsigned index
) {
482 return bound_t
{type_t::TEMP
, index
};
484 static bound_t
get_normal(unsigned index
) {
485 return bound_t
{type_t::NORMAL
, index
};
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
};
494 ghobject_t
get_oid(SeaStore
&seastore
, CollectionRef
&coll
) const {
499 return ghobject_t::get_max();
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
;
507 return make_oid(index
);
509 assert(0 == "impossible");
514 struct list_test_case_t
{
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
>>;
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
527 std::vector
<ghobject_t
> objs
;
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
);
536 for (unsigned i
= 0; i
< temp_to_create
; ++i
) {
537 create(make_temp_oid(i
));
539 for (unsigned i
= 0; i
< normal_to_create
; ++i
) {
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
);
548 // get results from seastore
549 auto [listed
, next
] = sharded_seastore
->list_objects(
550 coll
, left_bound
, right_bound
, limit
).get0();
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
;
559 auto correct_end
= correct_begin
;
560 for (; count
< limit
&&
561 correct_end
!= objs
.end() &&
562 *correct_end
< right_bound
;
563 ++correct_end
, ++count
);
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
);
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
);
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));
580 // we listed exactly limit objects
581 EXPECT_EQ(limit
, listed
.size());
583 EXPECT_GE(next
, left_bound
);
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
);
590 // next > *(correct_end - 1) since we already listed it
591 EXPECT_GT(next
, *(correct_end
- 1));
597 for (auto &&hoid
: objs
) { get_object(hoid
).remove(*sharded_seastore
); }
601 template <typename T
, typename V
>
602 auto contains(const T
&t
, const V
&v
) {
609 TEST_P(seastore_test_t
, collection_create_list_remove
)
612 coll_t test_coll
{spg_t
{pg_t
{1, 0}}};
614 sharded_seastore
->create_new_collection(test_coll
).get0();
617 t
.create_collection(test_coll
, 4);
618 do_transaction(std::move(t
));
620 auto colls_cores
= seastore
->list_collections().get0();
621 std::vector
<coll_t
> colls
;
622 colls
.resize(colls_cores
.size());
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
));
634 t
.remove_collection(test_coll
);
635 do_transaction(std::move(t
));
637 auto colls_cores
= seastore
->list_collections().get0();
638 std::vector
<coll_t
> colls
;
639 colls
.resize(colls_cores
.size());
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
));
649 TEST_P(seastore_test_t
, meta
) {
651 set_meta("key1", "value1");
652 set_meta("key2", "value2");
654 const auto [ret1
, value1
] = get_meta("key1");
655 const auto [ret2
, value2
] = get_meta("key2");
658 EXPECT_EQ(value1
, "value1");
659 EXPECT_EQ(value2
, "value2");
663 TEST_P(seastore_test_t
, touch_stat_list_remove
)
666 auto &test_obj
= get_object(make_oid(0));
667 test_obj
.touch(*sharded_seastore
);
668 test_obj
.check_size(*sharded_seastore
);
671 remove_object(test_obj
);
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() },
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) },
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() },
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() },
702 TEST_P(seastore_test_t
, list_objects_temp_only
)
704 run_async([this] { test_list(5, 0, temp_list_cases
); });
707 TEST_P(seastore_test_t
, list_objects_temp_overlap
)
709 run_async([this] { test_list(5, 5, temp_list_cases
); });
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() },
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)},
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() },
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() },
738 TEST_P(seastore_test_t
, list_objects_normal_only
)
740 run_async([this] { test_list(5, 0, normal_list_cases
); });
743 TEST_P(seastore_test_t
, list_objects_normal_overlap
)
745 run_async([this] { test_list(5, 5, normal_list_cases
); });
748 bufferlist
make_bufferlist(size_t len
) {
755 TEST_P(seastore_test_t
, omap_test_simple
)
758 auto &test_obj
= get_object(make_oid(0));
759 test_obj
.touch(*sharded_seastore
);
763 make_bufferlist(128));
764 test_obj
.check_omap_key(
770 TEST_P(seastore_test_t
, attr
)
773 auto& test_obj
= get_object(make_oid(0));
774 test_obj
.touch(*sharded_seastore
);
776 std::string
oi("asdfasdfasdf");
779 test_obj
.set_attr(*sharded_seastore
, OI_ATTR
, bl
);
781 std::string
ss("fdsfdsfs");
784 test_obj
.set_attr(*sharded_seastore
, SS_ATTR
, bl
);
786 std::string
test_val("ssssssssssss");
788 encode(test_val
, bl
);
789 test_obj
.set_attr(*sharded_seastore
, "test_key", bl
);
791 auto attrs
= test_obj
.get_attrs(*sharded_seastore
);
793 bufferlist bl2
= attrs
[OI_ATTR
];
796 bl2
= attrs
[SS_ATTR
];
799 std::string test_val2
;
801 bl2
= attrs
["test_key"];
802 decode(test_val2
, bl2
);
805 EXPECT_EQ(test_val
, test_val2
);
808 bl2
= test_obj
.get_attr(*sharded_seastore
, "test_key");
810 decode(test_val2
, bl2
);
811 EXPECT_EQ(test_val
, test_val2
);
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());
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
));
826 test_obj
.set_attr(*sharded_seastore
, OI_ATTR
, bl
);
828 char ss_array
[onode_layout_t::MAX_SS_LENGTH
+ 1] = {'b'};
829 std::string
ss_str(&ss_array
[0], sizeof(ss_array
));
832 test_obj
.set_attr(*sharded_seastore
, SS_ATTR
, bl
);
834 attrs
= test_obj
.get_attrs(*sharded_seastore
);
836 bl2
= attrs
[OI_ATTR
];
838 decode(oi_str2
, bl2
);
839 EXPECT_EQ(oi_str
, oi_str2
);
842 bl2
= attrs
[SS_ATTR
];
844 decode(ss_str2
, bl2
);
845 EXPECT_EQ(ss_str
, ss_str2
);
849 bl2
= test_obj
.get_attr(*sharded_seastore
, SS_ATTR
);
850 decode(ss_str2
, bl2
);
851 EXPECT_EQ(ss_str
, ss_str2
);
855 bl2
= test_obj
.get_attr(*sharded_seastore
, OI_ATTR
);
856 decode(oi_str2
, bl2
);
857 EXPECT_EQ(oi_str
, oi_str2
);
859 test_obj
.rm_attr(*sharded_seastore
, OI_ATTR
);
860 test_obj
.rm_attr(*sharded_seastore
, SS_ATTR
);
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());
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");
872 test_obj
.set_attr(*sharded_seastore
, OI_ATTR
, bl
);
877 test_obj
.set_attr(*sharded_seastore
, SS_ATTR
, bl
);
879 std::string
test_val("ssssssssssss");
881 encode(test_val
, bl
);
882 test_obj
.set_attr(*sharded_seastore
, "test_key", bl
);
884 auto attrs
= test_obj
.get_attrs(*sharded_seastore
);
886 bufferlist bl2
= attrs
[OI_ATTR
];
889 bl2
= attrs
[SS_ATTR
];
892 std::string test_val2
;
894 bl2
= attrs
["test_key"];
895 decode(test_val2
, bl2
);
898 EXPECT_EQ(test_val
, test_val2
);
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");
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());
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
));
918 test_obj
.set_attr(*sharded_seastore
, OI_ATTR
, bl
);
923 test_obj
.set_attr(*sharded_seastore
, OI_ATTR
, bl
);
925 char ss_array
[onode_layout_t::MAX_SS_LENGTH
+ 1] = {'b'};
926 std::string
ss(&ss_array
[0], sizeof(ss_array
));
929 test_obj
.set_attr(*sharded_seastore
, SS_ATTR
, bl
);
934 test_obj
.set_attr(*sharded_seastore
, SS_ATTR
, bl
);
936 auto attrs
= test_obj
.get_attrs(*sharded_seastore
);
937 std::string oi2
, ss2
;
938 bufferlist bl2
= attrs
[OI_ATTR
];
941 bl2
= attrs
[SS_ATTR
];
949 TEST_P(seastore_test_t
, omap_test_iterator
)
952 auto make_key
= [](unsigned i
) {
953 std::stringstream ss
;
957 auto &test_obj
= get_object(make_oid(0));
958 test_obj
.touch(*sharded_seastore
);
959 for (unsigned i
= 0; i
< 20; ++i
) {
963 make_bufferlist(128));
965 test_obj
.check_omap(*sharded_seastore
);
969 TEST_P(seastore_test_t
, object_data_omap_remove
)
972 auto make_key
= [](unsigned i
) {
973 std::stringstream ss
;
977 auto &test_obj
= get_object(make_oid(0));
978 test_obj
.touch(*sharded_seastore
);
979 for (unsigned i
= 0; i
< 1024; ++i
) {
983 make_bufferlist(128));
985 test_obj
.check_omap(*sharded_seastore
);
987 for (uint64_t i
= 0; i
< 16; i
++) {
994 test_obj
.remove(*sharded_seastore
);
999 TEST_P(seastore_test_t
, simple_extent_test
)
1002 auto &test_obj
= get_object(make_oid(0));
1012 test_obj
.check_size(*sharded_seastore
);
1016 TEST_P(seastore_test_t
, fiemap_empty
)
1019 auto &test_obj
= get_object(make_oid(0));
1020 test_obj
.touch(*sharded_seastore
);
1021 test_obj
.truncate(*sharded_seastore
, 100000);
1023 std::map
<uint64_t, uint64_t> m
;
1024 m
= test_obj
.fiemap(*sharded_seastore
, 0, 100000);
1025 EXPECT_TRUE(m
.empty());
1027 test_obj
.remove(*sharded_seastore
);
1031 TEST_P(seastore_test_t
, fiemap_holes
)
1034 const uint64_t MAX_EXTENTS
= 100;
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));
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
);
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());
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());
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);
1077 test_obj
.remove(*sharded_seastore
);
1081 TEST_P(seastore_test_t
, sparse_read
)
1084 const uint64_t MAX_EXTENTS
= 100;
1085 const uint64_t SKIP_STEP
= 16 << 10;
1086 auto &test_obj
= get_object(make_oid(0));
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
);
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());
1099 auto rbl
= test_obj
.readv(*sharded_seastore
, m
);
1101 for (auto &&miter
: m
) {
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
;
1107 test_obj
.remove(*sharded_seastore
);
1111 TEST_P(seastore_test_t
, zero
)
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
) {
1119 // Test zero within a block
1120 auto &test_obj
= get_object(make_oid(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');
1126 size
= off
+ (len
* (repeat
+ 1));
1132 test_obj
.check_size(*sharded_seastore
);
1133 test_obj
.zero(*sharded_seastore
, zero_off
, zero_len
);
1138 test_obj
.check_size(*sharded_seastore
);
1139 remove_object(test_obj
);
1142 const uint64_t BS
= 4<<10;
1144 // Test zero within a block
1146 {{1<<10, 1<<10, 1}},
1149 // Multiple writes, partial on left, partial on right.
1155 // Single large write, block boundary on right, partial on left.
1161 // Multiple writes, block boundary on left, partial on right.
1168 INSTANTIATE_TEST_SUITE_P(