]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include <boost/range/combine.hpp> | |
5 | ||
6 | #include "test/crimson/gtest_seastar.h" | |
7 | ||
8 | #include "test/crimson/seastore/transaction_manager_test_state.h" | |
9 | ||
10 | #include "crimson/os/seastore/onode_manager/staged-fltree/fltree_onode_manager.h" | |
11 | #include "crimson/os/seastore/onode_manager/staged-fltree/tree_utils.h" | |
12 | ||
13 | using namespace crimson; | |
14 | using namespace crimson::os; | |
15 | using namespace crimson::os::seastore; | |
16 | using namespace crimson::os::seastore::onode; | |
17 | using CTransaction = ceph::os::Transaction; | |
18 | using namespace std; | |
19 | ||
20 | namespace { | |
21 | [[maybe_unused]] seastar::logger& logger() { | |
22 | return crimson::get_logger(ceph_subsys_test); | |
23 | } | |
24 | } | |
25 | ||
26 | struct onode_item_t { | |
27 | uint32_t size; | |
28 | uint64_t id; | |
1e59de90 | 29 | uint64_t block_size; |
20effc67 TL |
30 | uint32_t cnt_modify = 0; |
31 | ||
32 | void initialize(Transaction& t, Onode& value) const { | |
33 | auto& layout = value.get_mutable_layout(t); | |
34 | layout.size = size; | |
1e59de90 TL |
35 | layout.omap_root.update(omap_root_t(id, cnt_modify, |
36 | value.get_metadata_hint(block_size))); | |
20effc67 TL |
37 | validate(value); |
38 | } | |
39 | ||
40 | void validate(Onode& value) const { | |
41 | auto& layout = value.get_layout(); | |
42 | ceph_assert(laddr_t(layout.size) == laddr_t{size}); | |
1e59de90 TL |
43 | ceph_assert(layout.omap_root.get(value.get_metadata_hint(block_size)).addr == id); |
44 | ceph_assert(layout.omap_root.get(value.get_metadata_hint(block_size)).depth == cnt_modify); | |
20effc67 TL |
45 | } |
46 | ||
47 | void modify(Transaction& t, Onode& value) { | |
48 | validate(value); | |
49 | ++cnt_modify; | |
50 | initialize(t, value); | |
51 | } | |
52 | ||
1e59de90 | 53 | static onode_item_t create(std::size_t size, std::size_t id, uint64_t block_size) { |
20effc67 | 54 | ceph_assert(size <= std::numeric_limits<uint32_t>::max()); |
1e59de90 | 55 | return {(uint32_t)size, id, block_size}; |
20effc67 TL |
56 | } |
57 | }; | |
58 | ||
59 | struct fltree_onode_manager_test_t | |
60 | : public seastar_test_suite_t, TMTestState { | |
61 | using iterator_t = typename KVPool<onode_item_t>::iterator_t; | |
62 | ||
63 | FLTreeOnodeManagerRef manager; | |
64 | ||
65 | seastar::future<> set_up_fut() final { | |
66 | return tm_setup(); | |
67 | } | |
68 | ||
69 | seastar::future<> tear_down_fut() final { | |
70 | return tm_teardown(); | |
71 | } | |
72 | ||
1e59de90 TL |
73 | virtual seastar::future<> _init() final { |
74 | return TMTestState::_init().then([this] { | |
75 | manager.reset(new FLTreeOnodeManager(*tm)); | |
76 | }); | |
20effc67 TL |
77 | } |
78 | ||
1e59de90 | 79 | virtual seastar::future<> _destroy() final { |
20effc67 | 80 | manager.reset(); |
1e59de90 | 81 | return TMTestState::_destroy(); |
20effc67 TL |
82 | } |
83 | ||
84 | virtual FuturizedStore::mkfs_ertr::future<> _mkfs() final { | |
85 | return TMTestState::_mkfs( | |
86 | ).safe_then([this] { | |
1e59de90 TL |
87 | return restart_fut(); |
88 | }).safe_then([this] { | |
89 | return repeat_eagain([this] { | |
90 | return seastar::do_with( | |
91 | create_mutate_transaction(), | |
92 | [this](auto &ref_t) | |
93 | { | |
94 | return with_trans_intr(*ref_t, [&](auto &t) { | |
95 | return manager->mkfs(t | |
96 | ).si_then([this, &t] { | |
97 | return submit_transaction_fut2(t); | |
98 | }); | |
99 | }); | |
100 | }); | |
101 | }); | |
102 | }).handle_error( | |
103 | crimson::ct_error::assert_all{"Invalid error in _mkfs"} | |
104 | ); | |
20effc67 TL |
105 | } |
106 | ||
107 | template <typename F> | |
108 | void with_transaction(F&& f) { | |
109 | auto t = create_mutate_transaction(); | |
110 | std::invoke(f, *t); | |
111 | submit_transaction(std::move(t)); | |
20effc67 TL |
112 | } |
113 | ||
114 | template <typename F> | |
115 | void with_onode_write(iterator_t& it, F&& f) { | |
116 | with_transaction([this, &it, f=std::move(f)] (auto& t) { | |
117 | auto p_kv = *it; | |
118 | auto onode = with_trans_intr(t, [&](auto &t) { | |
119 | return manager->get_or_create_onode(t, p_kv->key); | |
120 | }).unsafe_get0(); | |
121 | std::invoke(f, t, *onode, p_kv->value); | |
122 | with_trans_intr(t, [&](auto &t) { | |
123 | return manager->write_dirty(t, {onode}); | |
124 | }).unsafe_get0(); | |
125 | }); | |
126 | } | |
127 | ||
128 | void validate_onode(iterator_t& it) { | |
129 | with_transaction([this, &it] (auto& t) { | |
130 | auto p_kv = *it; | |
131 | auto onode = with_trans_intr(t, [&](auto &t) { | |
132 | return manager->get_onode(t, p_kv->key); | |
133 | }).unsafe_get0(); | |
134 | p_kv->value.validate(*onode); | |
135 | }); | |
136 | } | |
137 | ||
138 | void validate_erased(iterator_t& it) { | |
139 | with_transaction([this, &it] (auto& t) { | |
140 | auto p_kv = *it; | |
141 | auto exist = with_trans_intr(t, [&](auto &t) { | |
142 | return manager->contains_onode(t, p_kv->key); | |
143 | }).unsafe_get0(); | |
144 | ceph_assert(exist == false); | |
145 | }); | |
146 | } | |
147 | ||
148 | template <typename F> | |
149 | void with_onodes_process( | |
150 | const iterator_t& start, const iterator_t& end, F&& f) { | |
151 | std::vector<ghobject_t> oids; | |
152 | std::vector<onode_item_t*> items; | |
153 | auto it = start; | |
154 | while(it != end) { | |
155 | auto p_kv = *it; | |
156 | oids.emplace_back(p_kv->key); | |
157 | items.emplace_back(&p_kv->value); | |
158 | ++it; | |
159 | } | |
160 | with_transaction([&oids, &items, f=std::move(f)] (auto& t) mutable { | |
161 | std::invoke(f, t, oids, items); | |
162 | }); | |
163 | } | |
164 | ||
165 | template <typename F> | |
166 | void with_onodes_write( | |
167 | const iterator_t& start, const iterator_t& end, F&& f) { | |
168 | with_onodes_process(start, end, | |
169 | [this, f=std::move(f)] (auto& t, auto& oids, auto& items) { | |
170 | auto onodes = with_trans_intr(t, [&](auto &t) { | |
171 | return manager->get_or_create_onodes(t, oids); | |
172 | }).unsafe_get0(); | |
173 | for (auto tup : boost::combine(onodes, items)) { | |
174 | OnodeRef onode; | |
175 | onode_item_t* p_item; | |
176 | boost::tie(onode, p_item) = tup; | |
177 | std::invoke(f, t, *onode, *p_item); | |
178 | } | |
179 | with_trans_intr(t, [&](auto &t) { | |
180 | return manager->write_dirty(t, onodes); | |
181 | }).unsafe_get0(); | |
182 | }); | |
183 | } | |
184 | ||
185 | void validate_onodes( | |
186 | const iterator_t& start, const iterator_t& end) { | |
187 | with_onodes_process(start, end, | |
188 | [this] (auto& t, auto& oids, auto& items) { | |
189 | for (auto tup : boost::combine(oids, items)) { | |
190 | ghobject_t oid; | |
191 | onode_item_t* p_item; | |
192 | boost::tie(oid, p_item) = tup; | |
193 | auto onode = with_trans_intr(t, [&](auto &t) { | |
194 | return manager->get_onode(t, oid); | |
195 | }).unsafe_get0(); | |
196 | p_item->validate(*onode); | |
197 | } | |
198 | }); | |
199 | } | |
200 | ||
201 | void validate_erased( | |
202 | const iterator_t& start, const iterator_t& end) { | |
203 | with_onodes_process(start, end, | |
204 | [this] (auto& t, auto& oids, auto& items) { | |
205 | for (auto& oid : oids) { | |
206 | auto exist = with_trans_intr(t, [&](auto &t) { | |
207 | return manager->contains_onode(t, oid); | |
208 | }).unsafe_get0(); | |
209 | ceph_assert(exist == false); | |
210 | } | |
211 | }); | |
212 | } | |
213 | ||
214 | static constexpr uint64_t LIST_LIMIT = 10; | |
215 | void validate_list_onodes(KVPool<onode_item_t>& pool) { | |
216 | with_onodes_process(pool.begin(), pool.end(), | |
217 | [this] (auto& t, auto& oids, auto& items) { | |
218 | std::vector<ghobject_t> listed_oids; | |
219 | auto start = ghobject_t(); | |
220 | auto end = ghobject_t::get_max(); | |
221 | assert(start < end); | |
222 | assert(start < oids[0]); | |
223 | assert(oids[0] < end); | |
224 | while (start != end) { | |
225 | auto [list_ret, list_end] = with_trans_intr(t, [&](auto &t) { | |
226 | return manager->list_onodes(t, start, end, LIST_LIMIT); | |
227 | }).unsafe_get0(); | |
228 | listed_oids.insert(listed_oids.end(), list_ret.begin(), list_ret.end()); | |
229 | start = list_end; | |
230 | } | |
231 | ceph_assert(oids.size() == listed_oids.size()); | |
232 | }); | |
233 | } | |
234 | ||
235 | fltree_onode_manager_test_t() {} | |
236 | }; | |
237 | ||
238 | TEST_F(fltree_onode_manager_test_t, 1_single) | |
239 | { | |
240 | run_async([this] { | |
1e59de90 TL |
241 | uint64_t block_size = tm->get_block_size(); |
242 | auto pool = KVPool<onode_item_t>::create_range({0, 1}, {128, 256}, block_size); | |
20effc67 TL |
243 | auto iter = pool.begin(); |
244 | with_onode_write(iter, [](auto& t, auto& onode, auto& item) { | |
245 | item.initialize(t, onode); | |
246 | }); | |
247 | validate_onode(iter); | |
248 | ||
249 | with_onode_write(iter, [](auto& t, auto& onode, auto& item) { | |
250 | item.modify(t, onode); | |
251 | }); | |
252 | validate_onode(iter); | |
253 | ||
254 | validate_list_onodes(pool); | |
255 | ||
256 | with_onode_write(iter, [this](auto& t, auto& onode, auto& item) { | |
257 | OnodeRef onode_ref = &onode; | |
258 | with_trans_intr(t, [&](auto &t) { | |
259 | return manager->erase_onode(t, onode_ref); | |
260 | }).unsafe_get0(); | |
261 | }); | |
262 | validate_erased(iter); | |
263 | }); | |
264 | } | |
265 | ||
266 | TEST_F(fltree_onode_manager_test_t, 2_synthetic) | |
267 | { | |
268 | run_async([this] { | |
1e59de90 | 269 | uint64_t block_size = tm->get_block_size(); |
20effc67 | 270 | auto pool = KVPool<onode_item_t>::create_range( |
1e59de90 | 271 | {0, 100}, {32, 64, 128, 256, 512}, block_size); |
20effc67 TL |
272 | auto start = pool.begin(); |
273 | auto end = pool.end(); | |
274 | with_onodes_write(start, end, | |
275 | [](auto& t, auto& onode, auto& item) { | |
276 | item.initialize(t, onode); | |
277 | }); | |
278 | validate_onodes(start, end); | |
279 | ||
280 | validate_list_onodes(pool); | |
281 | ||
282 | auto rd_start = pool.random_begin(); | |
283 | auto rd_end = rd_start + 50; | |
284 | with_onodes_write(rd_start, rd_end, | |
285 | [](auto& t, auto& onode, auto& item) { | |
286 | item.modify(t, onode); | |
287 | }); | |
288 | validate_onodes(start, end); | |
289 | ||
290 | pool.shuffle(); | |
291 | rd_start = pool.random_begin(); | |
292 | rd_end = rd_start + 50; | |
293 | with_onodes_write(rd_start, rd_end, | |
294 | [](auto& t, auto& onode, auto& item) { | |
295 | item.modify(t, onode); | |
296 | }); | |
297 | validate_onodes(start, end); | |
298 | ||
299 | pool.shuffle(); | |
300 | rd_start = pool.random_begin(); | |
301 | rd_end = rd_start + 50; | |
302 | with_onodes_write(rd_start, rd_end, | |
303 | [this](auto& t, auto& onode, auto& item) { | |
304 | OnodeRef onode_ref = &onode; | |
305 | with_trans_intr(t, [&](auto &t) { | |
306 | return manager->erase_onode(t, onode_ref); | |
307 | }).unsafe_get0(); | |
308 | }); | |
309 | validate_erased(rd_start, rd_end); | |
310 | pool.erase_from_random(rd_start, rd_end); | |
311 | start = pool.begin(); | |
312 | end = pool.end(); | |
313 | validate_onodes(start, end); | |
314 | ||
315 | validate_list_onodes(pool); | |
316 | }); | |
317 | } |