]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "test/crimson/gtest_seastar.h" | |
5 | ||
6 | #include <random> | |
7 | ||
8 | #include "crimson/common/log.h" | |
1e59de90 | 9 | #include "crimson/os/seastore/async_cleaner.h" |
f67539c2 TL |
10 | #include "crimson/os/seastore/journal.h" |
11 | #include "crimson/os/seastore/segment_manager/ephemeral.h" | |
12 | ||
13 | using namespace crimson; | |
14 | using namespace crimson::os; | |
15 | using namespace crimson::os::seastore; | |
16 | ||
17 | namespace { | |
18 | [[maybe_unused]] seastar::logger& logger() { | |
19 | return crimson::get_logger(ceph_subsys_test); | |
20 | } | |
21 | } | |
22 | ||
23 | struct record_validator_t { | |
24 | record_t record; | |
25 | paddr_t record_final_offset; | |
26 | ||
27 | template <typename... T> | |
28 | record_validator_t(T&&... record) : record(std::forward<T>(record)...) {} | |
29 | ||
30 | void validate(SegmentManager &manager) { | |
31 | paddr_t addr = make_record_relative_paddr(0); | |
32 | for (auto &&block : record.extents) { | |
33 | auto test = manager.read( | |
34 | record_final_offset.add_relative(addr), | |
35 | block.bl.length()).unsafe_get0(); | |
1e59de90 | 36 | addr = addr.add_offset(block.bl.length()); |
f67539c2 TL |
37 | bufferlist bl; |
38 | bl.push_back(test); | |
39 | ASSERT_EQ( | |
40 | bl.length(), | |
41 | block.bl.length()); | |
42 | ASSERT_EQ( | |
43 | bl.begin().crc32c(bl.length(), 1), | |
44 | block.bl.begin().crc32c(block.bl.length(), 1)); | |
45 | } | |
46 | } | |
47 | ||
48 | auto get_replay_handler() { | |
49 | auto checker = [this, iter=record.deltas.begin()] ( | |
50 | paddr_t base, | |
51 | const delta_info_t &di) mutable { | |
52 | EXPECT_EQ(base, record_final_offset); | |
53 | ceph_assert(iter != record.deltas.end()); | |
54 | EXPECT_EQ(di, *iter++); | |
55 | EXPECT_EQ(base, record_final_offset); | |
56 | return iter != record.deltas.end(); | |
57 | }; | |
58 | if (record.deltas.size()) { | |
59 | return std::make_optional(std::move(checker)); | |
60 | } else { | |
61 | return std::optional<decltype(checker)>(); | |
62 | } | |
63 | } | |
64 | }; | |
65 | ||
1e59de90 | 66 | struct journal_test_t : seastar_test_suite_t, SegmentProvider, JournalTrimmer { |
f67539c2 | 67 | segment_manager::EphemeralSegmentManagerRef segment_manager; |
20effc67 | 68 | WritePipeline pipeline; |
1e59de90 | 69 | JournalRef journal; |
f67539c2 TL |
70 | |
71 | std::vector<record_validator_t> records; | |
72 | ||
73 | std::default_random_engine generator; | |
74 | ||
1e59de90 | 75 | extent_len_t block_size; |
f67539c2 | 76 | |
1e59de90 | 77 | SegmentManagerGroupRef sms; |
20effc67 TL |
78 | |
79 | segment_id_t next; | |
80 | ||
1e59de90 TL |
81 | std::map<segment_id_t, segment_seq_t> segment_seqs; |
82 | std::map<segment_id_t, segment_type_t> segment_types; | |
83 | ||
84 | journal_seq_t dummy_tail; | |
85 | ||
86 | mutable segment_info_t tmp_info; | |
87 | ||
20effc67 TL |
88 | journal_test_t() = default; |
89 | ||
1e59de90 TL |
90 | /* |
91 | * JournalTrimmer interfaces | |
92 | */ | |
93 | journal_seq_t get_journal_head() const final { return dummy_tail; } | |
94 | ||
95 | void set_journal_head(journal_seq_t) final {} | |
96 | ||
97 | journal_seq_t get_dirty_tail() const final { return dummy_tail; } | |
f67539c2 | 98 | |
1e59de90 TL |
99 | journal_seq_t get_alloc_tail() const final { return dummy_tail; } |
100 | ||
101 | void update_journal_tails(journal_seq_t, journal_seq_t) final {} | |
102 | ||
103 | bool try_reserve_inline_usage(std::size_t) final { return true; } | |
104 | ||
105 | void release_inline_usage(std::size_t) final {} | |
106 | ||
107 | std::size_t get_trim_size_per_cycle() const final { | |
108 | return 0; | |
109 | } | |
110 | ||
111 | /* | |
112 | * SegmentProvider interfaces | |
113 | */ | |
114 | const segment_info_t& get_seg_info(segment_id_t id) const final { | |
115 | tmp_info = {}; | |
116 | tmp_info.seq = segment_seqs.at(id); | |
117 | tmp_info.type = segment_types.at(id); | |
118 | return tmp_info; | |
119 | } | |
120 | ||
121 | segment_id_t allocate_segment( | |
122 | segment_seq_t seq, | |
123 | segment_type_t type, | |
124 | data_category_t, | |
125 | rewrite_gen_t | |
126 | ) final { | |
20effc67 TL |
127 | auto ret = next; |
128 | next = segment_id_t{ | |
1e59de90 | 129 | segment_manager->get_device_id(), |
20effc67 | 130 | next.device_segment_id() + 1}; |
1e59de90 TL |
131 | segment_seqs[ret] = seq; |
132 | segment_types[ret] = type; | |
133 | return ret; | |
f67539c2 TL |
134 | } |
135 | ||
1e59de90 TL |
136 | void close_segment(segment_id_t) final {} |
137 | ||
138 | void update_segment_avail_bytes(segment_type_t, paddr_t) final {} | |
139 | ||
140 | void update_modify_time(segment_id_t, sea_time_point, std::size_t) final {} | |
141 | ||
142 | SegmentManagerGroup* get_segment_manager_group() final { return sms.get(); } | |
f67539c2 TL |
143 | |
144 | seastar::future<> set_up_fut() final { | |
20effc67 | 145 | segment_manager = segment_manager::create_test_ephemeral(); |
f67539c2 TL |
146 | return segment_manager->init( |
147 | ).safe_then([this] { | |
1e59de90 TL |
148 | return segment_manager->mkfs( |
149 | segment_manager::get_ephemeral_device_config(0, 1, 0)); | |
150 | }).safe_then([this] { | |
151 | block_size = segment_manager->get_block_size(); | |
152 | sms.reset(new SegmentManagerGroup()); | |
153 | next = segment_id_t(segment_manager->get_device_id(), 0); | |
154 | journal = journal::make_segmented(*this, *this); | |
155 | journal->set_write_pipeline(&pipeline); | |
156 | sms->add_segment_manager(segment_manager.get()); | |
157 | return journal->open_for_mkfs(); | |
158 | }).safe_then([this](auto) { | |
159 | dummy_tail = journal_seq_t{0, | |
160 | paddr_t::make_seg_paddr(segment_id_t(segment_manager->get_device_id(), 0), 0)}; | |
161 | }, crimson::ct_error::all_same_way([] { | |
162 | ASSERT_FALSE("Unable to mount"); | |
163 | })); | |
f67539c2 TL |
164 | } |
165 | ||
20effc67 TL |
166 | seastar::future<> tear_down_fut() final { |
167 | return journal->close( | |
168 | ).safe_then([this] { | |
169 | segment_manager.reset(); | |
1e59de90 | 170 | sms.reset(); |
20effc67 TL |
171 | journal.reset(); |
172 | }).handle_error( | |
173 | crimson::ct_error::all_same_way([](auto e) { | |
174 | ASSERT_FALSE("Unable to close"); | |
175 | }) | |
176 | ); | |
177 | } | |
178 | ||
f67539c2 TL |
179 | template <typename T> |
180 | auto replay(T &&f) { | |
181 | return journal->close( | |
182 | ).safe_then([this, f=std::move(f)]() mutable { | |
1e59de90 | 183 | journal = journal::make_segmented(*this, *this); |
20effc67 | 184 | journal->set_write_pipeline(&pipeline); |
1e59de90 TL |
185 | return journal->replay(std::forward<T>(std::move(f))); |
186 | }).safe_then([this] { | |
187 | return journal->open_for_mount(); | |
f67539c2 TL |
188 | }); |
189 | } | |
190 | ||
191 | auto replay_and_check() { | |
192 | auto record_iter = records.begin(); | |
193 | decltype(record_iter->get_replay_handler()) delta_checker = std::nullopt; | |
194 | auto advance = [this, &record_iter, &delta_checker] { | |
195 | ceph_assert(!delta_checker); | |
196 | while (record_iter != records.end()) { | |
197 | auto checker = record_iter->get_replay_handler(); | |
198 | record_iter++; | |
199 | if (checker) { | |
200 | delta_checker.emplace(std::move(*checker)); | |
201 | break; | |
202 | } | |
203 | } | |
204 | }; | |
205 | advance(); | |
206 | replay( | |
207 | [&advance, | |
208 | &delta_checker] | |
1e59de90 TL |
209 | (const auto &offsets, |
210 | const auto &di, | |
211 | const journal_seq_t &, | |
212 | const journal_seq_t &, | |
213 | auto t) mutable { | |
f67539c2 TL |
214 | if (!delta_checker) { |
215 | EXPECT_FALSE("No Deltas Left"); | |
216 | } | |
20effc67 | 217 | if (!(*delta_checker)(offsets.record_block_base, di)) { |
f67539c2 TL |
218 | delta_checker = std::nullopt; |
219 | advance(); | |
220 | } | |
1e59de90 | 221 | return Journal::replay_ertr::make_ready_future<bool>(true); |
f67539c2 TL |
222 | }).unsafe_get0(); |
223 | ASSERT_EQ(record_iter, records.end()); | |
224 | for (auto &i : records) { | |
225 | i.validate(*segment_manager); | |
226 | } | |
227 | } | |
228 | ||
229 | template <typename... T> | |
230 | auto submit_record(T&&... _record) { | |
231 | auto record{std::forward<T>(_record)...}; | |
232 | records.push_back(record); | |
20effc67 TL |
233 | OrderingHandle handle = get_dummy_ordering_handle(); |
234 | auto [addr, _] = journal->submit_record( | |
235 | std::move(record), | |
236 | handle).unsafe_get0(); | |
f67539c2 TL |
237 | records.back().record_final_offset = addr; |
238 | return addr; | |
239 | } | |
240 | ||
f67539c2 TL |
241 | extent_t generate_extent(size_t blocks) { |
242 | std::uniform_int_distribution<char> distribution( | |
243 | std::numeric_limits<char>::min(), | |
244 | std::numeric_limits<char>::max() | |
245 | ); | |
246 | char contents = distribution(generator); | |
247 | bufferlist bl; | |
248 | bl.append(buffer::ptr(buffer::create(blocks * block_size, contents))); | |
1e59de90 TL |
249 | return extent_t{ |
250 | extent_types_t::TEST_BLOCK, | |
251 | L_ADDR_NULL, | |
252 | bl}; | |
f67539c2 TL |
253 | } |
254 | ||
255 | delta_info_t generate_delta(size_t bytes) { | |
256 | std::uniform_int_distribution<char> distribution( | |
257 | std::numeric_limits<char>::min(), | |
258 | std::numeric_limits<char>::max() | |
259 | ); | |
260 | char contents = distribution(generator); | |
261 | bufferlist bl; | |
262 | bl.append(buffer::ptr(buffer::create(bytes, contents))); | |
263 | return delta_info_t{ | |
264 | extent_types_t::TEST_BLOCK, | |
265 | paddr_t{}, | |
266 | L_ADDR_NULL, | |
267 | 0, 0, | |
268 | block_size, | |
269 | 1, | |
1e59de90 TL |
270 | MAX_SEG_SEQ, |
271 | segment_type_t::NULL_SEG, | |
f67539c2 TL |
272 | bl |
273 | }; | |
274 | } | |
275 | }; | |
276 | ||
277 | TEST_F(journal_test_t, replay_one_journal_segment) | |
278 | { | |
279 | run_async([this] { | |
280 | submit_record(record_t{ | |
281 | { generate_extent(1), generate_extent(2) }, | |
282 | { generate_delta(23), generate_delta(30) } | |
283 | }); | |
284 | replay_and_check(); | |
285 | }); | |
286 | } | |
287 | ||
288 | TEST_F(journal_test_t, replay_two_records) | |
289 | { | |
290 | run_async([this] { | |
291 | submit_record(record_t{ | |
292 | { generate_extent(1), generate_extent(2) }, | |
293 | { generate_delta(23), generate_delta(30) } | |
294 | }); | |
295 | submit_record(record_t{ | |
296 | { generate_extent(4), generate_extent(1) }, | |
297 | { generate_delta(23), generate_delta(400) } | |
298 | }); | |
299 | replay_and_check(); | |
300 | }); | |
301 | } | |
302 | ||
303 | TEST_F(journal_test_t, replay_twice) | |
304 | { | |
305 | run_async([this] { | |
306 | submit_record(record_t{ | |
307 | { generate_extent(1), generate_extent(2) }, | |
308 | { generate_delta(23), generate_delta(30) } | |
309 | }); | |
310 | submit_record(record_t{ | |
311 | { generate_extent(4), generate_extent(1) }, | |
312 | { generate_delta(23), generate_delta(400) } | |
313 | }); | |
314 | replay_and_check(); | |
315 | submit_record(record_t{ | |
316 | { generate_extent(2), generate_extent(5) }, | |
317 | { generate_delta(230), generate_delta(40) } | |
318 | }); | |
319 | replay_and_check(); | |
320 | }); | |
321 | } | |
322 | ||
323 | TEST_F(journal_test_t, roll_journal_and_replay) | |
324 | { | |
325 | run_async([this] { | |
326 | paddr_t current = submit_record( | |
327 | record_t{ | |
328 | { generate_extent(1), generate_extent(2) }, | |
329 | { generate_delta(23), generate_delta(30) } | |
330 | }); | |
20effc67 | 331 | auto starting_segment = current.as_seg_paddr().get_segment_id(); |
f67539c2 | 332 | unsigned so_far = 0; |
20effc67 | 333 | while (current.as_seg_paddr().get_segment_id() == starting_segment) { |
f67539c2 TL |
334 | current = submit_record(record_t{ |
335 | { generate_extent(512), generate_extent(512) }, | |
336 | { generate_delta(23), generate_delta(400) } | |
337 | }); | |
338 | ++so_far; | |
339 | ASSERT_FALSE(so_far > 10); | |
340 | } | |
341 | replay_and_check(); | |
342 | }); | |
343 | } |