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
,
61 coll_t coll_name
{spg_t
{pg_t
{0, 0}}};
66 seastar::future
<> set_up_fut() final
{
69 return sharded_seastore
->create_new_collection(coll_name
);
70 }).then([this](auto coll_ref
) {
73 t
.create_collection(coll_name
, 0);
74 return sharded_seastore
->do_transaction(
80 seastar::future
<> tear_down_fut() final
{
85 void do_transaction(CTransaction
&&t
) {
86 return sharded_seastore
->do_transaction(
92 const std::string
& key
,
93 const std::string
& value
) {
94 return seastore
->write_meta(key
, value
).get0();
97 std::tuple
<int, std::string
> get_meta(
98 const std::string
& key
) {
99 return seastore
->read_meta(key
).get();
102 struct object_state_t
{
104 const CollectionRef coll
;
105 const ghobject_t oid
;
107 std::map
<string
, bufferlist
> omap
;
110 std::map
<snapid_t
, bufferlist
> clone_contents
;
118 SeaStoreShard
&sharded_seastore
) {
121 sharded_seastore
.do_transaction(
123 std::move(t
)).get0();
129 t
.truncate(cid
, oid
, off
);
133 SeaStoreShard
&sharded_seastore
,
137 sharded_seastore
.do_transaction(
139 std::move(t
)).get0();
142 std::map
<uint64_t, uint64_t> fiemap(
143 SeaStoreShard
&sharded_seastore
,
146 return sharded_seastore
.fiemap(coll
, oid
, off
, len
).unsafe_get0();
150 SeaStoreShard
&sharded_seastore
,
151 interval_set
<uint64_t>&m
) {
152 return sharded_seastore
.readv(coll
, oid
, m
).unsafe_get0();
158 t
.remove_collection(cid
);
162 SeaStoreShard
&sharded_seastore
) {
165 sharded_seastore
.do_transaction(
167 std::move(t
)).get0();
173 const bufferlist
&val
) {
175 std::map
<string
, bufferlist
> arg
;
184 SeaStoreShard
&sharded_seastore
,
186 const bufferlist
&val
) {
188 set_omap(t
, key
, val
);
189 sharded_seastore
.do_transaction(
191 std::move(t
)).get0();
195 SeaStoreShard
&sharded_seastore
,
199 bufferlist new_contents
;
200 if (offset
> 0 && contents
.length()) {
201 new_contents
.substr_of(
204 std::min
<size_t>(offset
, contents
.length())
207 new_contents
.append_zero(offset
- new_contents
.length());
208 new_contents
.append(bl
);
210 auto tail_offset
= offset
+ bl
.length();
211 if (contents
.length() > tail_offset
) {
216 contents
.length() - tail_offset
);
217 new_contents
.append(tail
);
219 contents
.swap(new_contents
);
230 SeaStoreShard
&sharded_seastore
,
234 write(sharded_seastore
, t
, offset
, bl
);
235 sharded_seastore
.do_transaction(
237 std::move(t
)).get0();
241 SeaStoreShard
&sharded_seastore
,
243 ghobject_t coid
= oid
;
244 coid
.hobj
.snap
= snap
;
246 t
.clone(cid
, oid
, coid
);
247 sharded_seastore
.do_transaction(
249 std::move(t
)).get0();
250 clone_contents
[snap
].reserve(contents
.length());
251 auto it
= contents
.begin();
252 it
.copy_all(clone_contents
[snap
]);
255 object_state_t
get_clone(snapid_t snap
) {
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
);
266 SeaStoreShard
&sharded_seastore
,
270 auto buffer
= bufferptr(buffer::create(len
));
271 ::memset(buffer
.c_str(), fill
, len
);
274 write(sharded_seastore
, offset
, bl
);
278 SeaStoreShard
&sharded_seastore
,
282 ceph::buffer::list bl
;
284 bufferlist new_contents
;
285 if (offset
> 0 && contents
.length()) {
286 new_contents
.substr_of(
289 std::min
<size_t>(offset
, contents
.length())
292 new_contents
.append_zero(offset
- new_contents
.length());
293 new_contents
.append(bl
);
295 auto tail_offset
= offset
+ bl
.length();
296 if (contents
.length() > tail_offset
) {
301 contents
.length() - tail_offset
);
302 new_contents
.append(tail
);
304 contents
.swap(new_contents
);
314 SeaStoreShard
&sharded_seastore
,
318 zero(sharded_seastore
, t
, offset
, len
);
319 sharded_seastore
.do_transaction(
321 std::move(t
)).get0();
325 SeaStoreShard
&sharded_seastore
,
329 if (contents
.length() >= offset
) {
333 std::min(len
, (uint64_t)contents
.length()));
335 auto ret
= sharded_seastore
.read(
340 EXPECT_EQ(ret
.length(), to_check
.length());
341 EXPECT_EQ(ret
, to_check
);
344 void check_size(SeaStoreShard
&sharded_seastore
) {
345 auto st
= sharded_seastore
.stat(
348 EXPECT_EQ(contents
.length(), st
.st_size
);
352 SeaStoreShard
&sharded_seastore
,
356 t
.setattr(cid
, oid
, key
, val
);
357 sharded_seastore
.do_transaction(
359 std::move(t
)).get0();
363 SeaStoreShard
&sharded_seastore
,
366 t
.rmattr(cid
, oid
, key
);
367 sharded_seastore
.do_transaction(
369 std::move(t
)).get0();
373 SeaStoreShard
&sharded_seastore
) {
376 sharded_seastore
.do_transaction(
378 std::move(t
)).get0();
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
{})
388 ceph::bufferlist
get_attr(
389 SeaStoreShard
& sharded_seastore
,
390 std::string_view name
) {
391 return sharded_seastore
.get_attr(coll
, oid
, name
)
393 SeaStoreShard::get_attr_errorator::discard_all
{})
398 SeaStoreShard
&sharded_seastore
,
400 std::set
<string
> to_check
;
401 to_check
.insert(key
);
402 auto result
= sharded_seastore
.omap_get_values(
405 to_check
).unsafe_get0();
406 if (result
.empty()) {
407 EXPECT_EQ(omap
.find(key
), omap
.end());
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
);
418 void check_omap(SeaStoreShard
&sharded_seastore
) {
419 auto refiter
= omap
.begin();
420 std::optional
<std::string
> start
;
422 auto [done
, kvs
] = sharded_seastore
.omap_get_values(
425 start
).unsafe_get0();
426 auto iter
= kvs
.begin();
428 if ((done
&& iter
== kvs
.end()) && refiter
== omap
.end()) {
430 } else if (!done
&& iter
== kvs
.end()) {
433 if (iter
== kvs
.end() || refiter
->first
< iter
->first
) {
435 "check_omap: missing omap key {}",
437 GTEST_FAIL() << "missing omap key " << refiter
->first
;
439 } else if (refiter
== omap
.end() || refiter
->first
> iter
->first
) {
441 "check_omap: extra omap key {}",
443 GTEST_FAIL() << "extra omap key " << iter
->first
;
446 EXPECT_EQ(iter
->second
, refiter
->second
);
452 start
= kvs
.rbegin()->first
;
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(
464 object_state_t
{coll_name
, coll
, oid
})).first
->second
;
468 object_state_t
&sobj
) {
470 sobj
.remove(*sharded_seastore
);
471 auto erased
= test_objects
.erase(sobj
.oid
);
472 ceph_assert(erased
== 1);
475 void validate_objects() const {
476 std::vector
<ghobject_t
> oids
;
477 for (auto& [oid
, obj
] : test_objects
) {
478 oids
.emplace_back(oid
);
480 auto ret
= sharded_seastore
->list_objects(
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
);
489 // create temp objects
498 } type
= type_t::MIN
;
501 static bound_t
get_temp(unsigned index
) {
502 return bound_t
{type_t::TEMP
, index
};
504 static bound_t
get_normal(unsigned index
) {
505 return bound_t
{type_t::NORMAL
, index
};
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
};
514 ghobject_t
get_oid(SeaStore
&seastore
, CollectionRef
&coll
) const {
519 return ghobject_t::get_max();
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
;
527 return make_oid(index
);
529 assert(0 == "impossible");
534 struct list_test_case_t
{
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
>>;
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
547 std::vector
<ghobject_t
> objs
;
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
);
556 for (unsigned i
= 0; i
< temp_to_create
; ++i
) {
557 create(make_temp_oid(i
));
559 for (unsigned i
= 0; i
< normal_to_create
; ++i
) {
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
);
568 // get results from seastore
569 auto [listed
, next
] = sharded_seastore
->list_objects(
570 coll
, left_bound
, right_bound
, limit
).get0();
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
;
579 auto correct_end
= correct_begin
;
580 for (; count
< limit
&&
581 correct_end
!= objs
.end() &&
582 *correct_end
< right_bound
;
583 ++correct_end
, ++count
);
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
);
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
);
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));
600 // we listed exactly limit objects
601 EXPECT_EQ(limit
, listed
.size());
603 EXPECT_GE(next
, left_bound
);
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
);
610 // next > *(correct_end - 1) since we already listed it
611 EXPECT_GT(next
, *(correct_end
- 1));
617 for (auto &&hoid
: objs
) { get_object(hoid
).remove(*sharded_seastore
); }
621 template <typename T
, typename V
>
622 auto contains(const T
&t
, const V
&v
) {
629 TEST_P(seastore_test_t
, collection_create_list_remove
)
632 coll_t test_coll
{spg_t
{pg_t
{1, 0}}};
634 sharded_seastore
->create_new_collection(test_coll
).get0();
637 t
.create_collection(test_coll
, 4);
638 do_transaction(std::move(t
));
640 auto colls_cores
= seastore
->list_collections().get0();
641 std::vector
<coll_t
> colls
;
642 colls
.resize(colls_cores
.size());
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
));
654 t
.remove_collection(test_coll
);
655 do_transaction(std::move(t
));
657 auto colls_cores
= seastore
->list_collections().get0();
658 std::vector
<coll_t
> colls
;
659 colls
.resize(colls_cores
.size());
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
));
669 TEST_P(seastore_test_t
, meta
) {
671 set_meta("key1", "value1");
672 set_meta("key2", "value2");
674 const auto [ret1
, value1
] = get_meta("key1");
675 const auto [ret2
, value2
] = get_meta("key2");
678 EXPECT_EQ(value1
, "value1");
679 EXPECT_EQ(value2
, "value2");
683 TEST_P(seastore_test_t
, touch_stat_list_remove
)
686 auto &test_obj
= get_object(make_oid(0));
687 test_obj
.touch(*sharded_seastore
);
688 test_obj
.check_size(*sharded_seastore
);
691 remove_object(test_obj
);
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() },
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) },
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() },
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() },
722 TEST_P(seastore_test_t
, list_objects_temp_only
)
724 run_async([this] { test_list(5, 0, temp_list_cases
); });
727 TEST_P(seastore_test_t
, list_objects_temp_overlap
)
729 run_async([this] { test_list(5, 5, temp_list_cases
); });
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() },
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)},
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() },
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() },
758 TEST_P(seastore_test_t
, list_objects_normal_only
)
760 run_async([this] { test_list(5, 0, normal_list_cases
); });
763 TEST_P(seastore_test_t
, list_objects_normal_overlap
)
765 run_async([this] { test_list(5, 5, normal_list_cases
); });
768 bufferlist
make_bufferlist(size_t len
) {
775 TEST_P(seastore_test_t
, omap_test_simple
)
778 auto &test_obj
= get_object(make_oid(0));
779 test_obj
.touch(*sharded_seastore
);
783 make_bufferlist(128));
784 test_obj
.check_omap_key(
790 TEST_P(seastore_test_t
, clone_aligned_extents
)
793 auto &test_obj
= get_object(make_oid(0));
794 test_obj
.write(*sharded_seastore
, 0, 4096, 'a');
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);
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);
822 TEST_P(seastore_test_t
, clone_unaligned_extents
)
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');
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);
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);
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);
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);
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);
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);
863 TEST_P(seastore_test_t
, attr
)
866 auto& test_obj
= get_object(make_oid(0));
867 test_obj
.touch(*sharded_seastore
);
869 std::string
oi("asdfasdfasdf");
872 test_obj
.set_attr(*sharded_seastore
, OI_ATTR
, bl
);
874 std::string
ss("fdsfdsfs");
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
);
901 bl2
= test_obj
.get_attr(*sharded_seastore
, "test_key");
903 decode(test_val2
, bl2
);
904 EXPECT_EQ(test_val
, test_val2
);
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());
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
));
919 test_obj
.set_attr(*sharded_seastore
, OI_ATTR
, bl
);
921 char ss_array
[onode_layout_t::MAX_SS_LENGTH
+ 1] = {'b'};
922 std::string
ss_str(&ss_array
[0], sizeof(ss_array
));
925 test_obj
.set_attr(*sharded_seastore
, SS_ATTR
, bl
);
927 attrs
= test_obj
.get_attrs(*sharded_seastore
);
929 bl2
= attrs
[OI_ATTR
];
931 decode(oi_str2
, bl2
);
932 EXPECT_EQ(oi_str
, oi_str2
);
935 bl2
= attrs
[SS_ATTR
];
937 decode(ss_str2
, bl2
);
938 EXPECT_EQ(ss_str
, ss_str2
);
942 bl2
= test_obj
.get_attr(*sharded_seastore
, SS_ATTR
);
943 decode(ss_str2
, bl2
);
944 EXPECT_EQ(ss_str
, ss_str2
);
948 bl2
= test_obj
.get_attr(*sharded_seastore
, OI_ATTR
);
949 decode(oi_str2
, bl2
);
950 EXPECT_EQ(oi_str
, oi_str2
);
952 test_obj
.rm_attr(*sharded_seastore
, OI_ATTR
);
953 test_obj
.rm_attr(*sharded_seastore
, SS_ATTR
);
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());
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");
965 test_obj
.set_attr(*sharded_seastore
, OI_ATTR
, bl
);
970 test_obj
.set_attr(*sharded_seastore
, SS_ATTR
, bl
);
972 std::string
test_val("ssssssssssss");
974 encode(test_val
, bl
);
975 test_obj
.set_attr(*sharded_seastore
, "test_key", bl
);
977 auto attrs
= test_obj
.get_attrs(*sharded_seastore
);
979 bufferlist bl2
= attrs
[OI_ATTR
];
982 bl2
= attrs
[SS_ATTR
];
985 std::string test_val2
;
987 bl2
= attrs
["test_key"];
988 decode(test_val2
, bl2
);
991 EXPECT_EQ(test_val
, test_val2
);
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");
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());
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
));
1011 test_obj
.set_attr(*sharded_seastore
, OI_ATTR
, bl
);
1013 oi
= "asdfasdfasdf";
1016 test_obj
.set_attr(*sharded_seastore
, OI_ATTR
, bl
);
1018 char ss_array
[onode_layout_t::MAX_SS_LENGTH
+ 1] = {'b'};
1019 std::string
ss(&ss_array
[0], sizeof(ss_array
));
1022 test_obj
.set_attr(*sharded_seastore
, SS_ATTR
, bl
);
1027 test_obj
.set_attr(*sharded_seastore
, SS_ATTR
, bl
);
1029 auto attrs
= test_obj
.get_attrs(*sharded_seastore
);
1030 std::string oi2
, ss2
;
1031 bufferlist bl2
= attrs
[OI_ATTR
];
1034 bl2
= attrs
[SS_ATTR
];
1042 TEST_P(seastore_test_t
, omap_test_iterator
)
1045 auto make_key
= [](unsigned i
) {
1046 std::stringstream ss
;
1050 auto &test_obj
= get_object(make_oid(0));
1051 test_obj
.touch(*sharded_seastore
);
1052 for (unsigned i
= 0; i
< 20; ++i
) {
1056 make_bufferlist(128));
1058 test_obj
.check_omap(*sharded_seastore
);
1062 TEST_P(seastore_test_t
, object_data_omap_remove
)
1065 auto make_key
= [](unsigned i
) {
1066 std::stringstream ss
;
1070 auto &test_obj
= get_object(make_oid(0));
1071 test_obj
.touch(*sharded_seastore
);
1072 for (unsigned i
= 0; i
< 1024; ++i
) {
1076 make_bufferlist(128));
1078 test_obj
.check_omap(*sharded_seastore
);
1080 for (uint64_t i
= 0; i
< 16; i
++) {
1087 test_obj
.remove(*sharded_seastore
);
1092 TEST_P(seastore_test_t
, simple_extent_test
)
1095 auto &test_obj
= get_object(make_oid(0));
1105 test_obj
.check_size(*sharded_seastore
);
1109 TEST_P(seastore_test_t
, fiemap_empty
)
1112 auto &test_obj
= get_object(make_oid(0));
1113 test_obj
.touch(*sharded_seastore
);
1114 test_obj
.truncate(*sharded_seastore
, 100000);
1116 std::map
<uint64_t, uint64_t> m
;
1117 m
= test_obj
.fiemap(*sharded_seastore
, 0, 100000);
1118 EXPECT_TRUE(m
.empty());
1120 test_obj
.remove(*sharded_seastore
);
1124 TEST_P(seastore_test_t
, fiemap_holes
)
1127 const uint64_t MAX_EXTENTS
= 100;
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));
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
);
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());
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());
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);
1170 test_obj
.remove(*sharded_seastore
);
1174 TEST_P(seastore_test_t
, sparse_read
)
1177 const uint64_t MAX_EXTENTS
= 100;
1178 const uint64_t SKIP_STEP
= 16 << 10;
1179 auto &test_obj
= get_object(make_oid(0));
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
);
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());
1192 auto rbl
= test_obj
.readv(*sharded_seastore
, m
);
1194 for (auto &&miter
: m
) {
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
;
1200 test_obj
.remove(*sharded_seastore
);
1204 TEST_P(seastore_test_t
, zero
)
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
) {
1212 // Test zero within a block
1213 auto &test_obj
= get_object(make_oid(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');
1219 size
= off
+ (len
* (repeat
+ 1));
1225 test_obj
.check_size(*sharded_seastore
);
1226 test_obj
.zero(*sharded_seastore
, zero_off
, zero_len
);
1231 test_obj
.check_size(*sharded_seastore
);
1232 remove_object(test_obj
);
1235 const uint64_t BS
= 4<<10;
1237 // Test zero within a block
1239 {{1<<10, 1<<10, 1}},
1242 // Multiple writes, partial on left, partial on right.
1248 // Single large write, block boundary on right, partial on left.
1254 // Multiple writes, block boundary on left, partial on right.
1261 INSTANTIATE_TEST_SUITE_P(