]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2015 Red Hat | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | #include "rgw/rgw_period_history.h" | |
15 | #include "rgw/rgw_rados.h" | |
11fdf7f2 | 16 | #include "rgw/rgw_zone.h" |
7c673cae FG |
17 | #include "global/global_init.h" |
18 | #include "common/ceph_argparse.h" | |
19 | #include <boost/lexical_cast.hpp> | |
20 | #include <gtest/gtest.h> | |
21 | ||
20effc67 | 22 | using namespace std; |
7c673cae FG |
23 | namespace { |
24 | ||
25 | // construct a period with the given fields | |
26 | RGWPeriod make_period(const std::string& id, epoch_t realm_epoch, | |
27 | const std::string& predecessor) | |
28 | { | |
29 | RGWPeriod period(id); | |
30 | period.set_realm_epoch(realm_epoch); | |
31 | period.set_predecessor(predecessor); | |
32 | return period; | |
33 | } | |
34 | ||
35 | const auto current_period = make_period("5", 5, "4"); | |
36 | ||
37 | // mock puller that throws an exception if it's called | |
38 | struct ErrorPuller : public RGWPeriodHistory::Puller { | |
b3b6e05e | 39 | int pull(const DoutPrefixProvider *dpp, const std::string& id, RGWPeriod& period, optional_yield) override { |
7c673cae FG |
40 | throw std::runtime_error("unexpected call to pull"); |
41 | } | |
42 | }; | |
43 | ErrorPuller puller; // default puller | |
44 | ||
45 | // mock puller that records the period ids requested and returns an error | |
46 | using Ids = std::vector<std::string>; | |
47 | class RecordingPuller : public RGWPeriodHistory::Puller { | |
48 | const int error; | |
49 | public: | |
11fdf7f2 | 50 | explicit RecordingPuller(int error) : error(error) {} |
7c673cae | 51 | Ids ids; |
b3b6e05e | 52 | int pull(const DoutPrefixProvider *dpp, const std::string& id, RGWPeriod& period, optional_yield) override { |
7c673cae FG |
53 | ids.push_back(id); |
54 | return error; | |
55 | } | |
56 | }; | |
57 | ||
58 | // mock puller that returns a fake period by parsing the period id | |
59 | struct NumericPuller : public RGWPeriodHistory::Puller { | |
b3b6e05e | 60 | int pull(const DoutPrefixProvider *dpp, const std::string& id, RGWPeriod& period, optional_yield) override { |
7c673cae FG |
61 | // relies on numeric period ids to divine the realm_epoch |
62 | auto realm_epoch = boost::lexical_cast<epoch_t>(id); | |
63 | auto predecessor = boost::lexical_cast<std::string>(realm_epoch-1); | |
64 | period = make_period(id, realm_epoch, predecessor); | |
65 | return 0; | |
66 | } | |
67 | }; | |
68 | ||
69 | } // anonymous namespace | |
70 | ||
71 | // for ASSERT_EQ() | |
72 | bool operator==(const RGWPeriod& lhs, const RGWPeriod& rhs) | |
73 | { | |
74 | return lhs.get_id() == rhs.get_id() | |
75 | && lhs.get_realm_epoch() == rhs.get_realm_epoch(); | |
76 | } | |
77 | ||
78 | TEST(PeriodHistory, InsertBefore) | |
79 | { | |
80 | RGWPeriodHistory history(g_ceph_context, &puller, current_period); | |
81 | ||
82 | // inserting right before current_period 5 will attach to history | |
83 | auto c = history.insert(make_period("4", 4, "3")); | |
84 | ASSERT_TRUE(c); | |
85 | ASSERT_FALSE(c.has_prev()); | |
86 | ASSERT_TRUE(c.has_next()); | |
87 | ||
88 | // cursor can traverse forward to current_period | |
89 | c.next(); | |
90 | ASSERT_EQ(5u, c.get_epoch()); | |
91 | ASSERT_EQ(current_period, c.get_period()); | |
92 | } | |
93 | ||
94 | TEST(PeriodHistory, InsertAfter) | |
95 | { | |
96 | RGWPeriodHistory history(g_ceph_context, &puller, current_period); | |
97 | ||
98 | // inserting right after current_period 5 will attach to history | |
99 | auto c = history.insert(make_period("6", 6, "5")); | |
100 | ASSERT_TRUE(c); | |
101 | ASSERT_TRUE(c.has_prev()); | |
102 | ASSERT_FALSE(c.has_next()); | |
103 | ||
104 | // cursor can traverse back to current_period | |
105 | c.prev(); | |
106 | ASSERT_EQ(5u, c.get_epoch()); | |
107 | ASSERT_EQ(current_period, c.get_period()); | |
108 | } | |
109 | ||
110 | TEST(PeriodHistory, InsertWayBefore) | |
111 | { | |
112 | RGWPeriodHistory history(g_ceph_context, &puller, current_period); | |
113 | ||
114 | // inserting way before current_period 5 will not attach to history | |
115 | auto c = history.insert(make_period("1", 1, "")); | |
116 | ASSERT_FALSE(c); | |
117 | ASSERT_EQ(0, c.get_error()); | |
118 | } | |
119 | ||
120 | TEST(PeriodHistory, InsertWayAfter) | |
121 | { | |
122 | RGWPeriodHistory history(g_ceph_context, &puller, current_period); | |
123 | ||
124 | // inserting way after current_period 5 will not attach to history | |
125 | auto c = history.insert(make_period("9", 9, "8")); | |
126 | ASSERT_FALSE(c); | |
127 | ASSERT_EQ(0, c.get_error()); | |
128 | } | |
129 | ||
130 | TEST(PeriodHistory, PullPredecessorsBeforeCurrent) | |
131 | { | |
132 | RecordingPuller puller{-EFAULT}; | |
133 | RGWPeriodHistory history(g_ceph_context, &puller, current_period); | |
b3b6e05e | 134 | const DoutPrefix dp(g_ceph_context, 1, "test rgw period history: "); |
7c673cae FG |
135 | |
136 | // create a disjoint history at 1 and verify that periods are requested | |
137 | // backwards from current_period | |
b3b6e05e | 138 | auto c1 = history.attach(&dp, make_period("1", 1, ""), null_yield); |
7c673cae FG |
139 | ASSERT_FALSE(c1); |
140 | ASSERT_EQ(-EFAULT, c1.get_error()); | |
141 | ASSERT_EQ(Ids{"4"}, puller.ids); | |
142 | ||
143 | auto c4 = history.insert(make_period("4", 4, "3")); | |
144 | ASSERT_TRUE(c4); | |
145 | ||
b3b6e05e | 146 | c1 = history.attach(&dp, make_period("1", 1, ""), null_yield); |
7c673cae FG |
147 | ASSERT_FALSE(c1); |
148 | ASSERT_EQ(-EFAULT, c1.get_error()); | |
149 | ASSERT_EQ(Ids({"4", "3"}), puller.ids); | |
150 | ||
151 | auto c3 = history.insert(make_period("3", 3, "2")); | |
152 | ASSERT_TRUE(c3); | |
153 | ||
b3b6e05e | 154 | c1 = history.attach(&dp, make_period("1", 1, ""), null_yield); |
7c673cae FG |
155 | ASSERT_FALSE(c1); |
156 | ASSERT_EQ(-EFAULT, c1.get_error()); | |
157 | ASSERT_EQ(Ids({"4", "3", "2"}), puller.ids); | |
158 | ||
159 | auto c2 = history.insert(make_period("2", 2, "1")); | |
160 | ASSERT_TRUE(c2); | |
161 | ||
b3b6e05e | 162 | c1 = history.attach(&dp, make_period("1", 1, ""), null_yield); |
7c673cae FG |
163 | ASSERT_TRUE(c1); |
164 | ASSERT_EQ(Ids({"4", "3", "2"}), puller.ids); | |
165 | } | |
166 | ||
167 | TEST(PeriodHistory, PullPredecessorsAfterCurrent) | |
168 | { | |
169 | RecordingPuller puller{-EFAULT}; | |
170 | RGWPeriodHistory history(g_ceph_context, &puller, current_period); | |
b3b6e05e | 171 | const DoutPrefix dp(g_ceph_context, 1, "test rgw period history: "); |
7c673cae FG |
172 | |
173 | // create a disjoint history at 9 and verify that periods are requested | |
174 | // backwards down to current_period | |
b3b6e05e | 175 | auto c9 = history.attach(&dp, make_period("9", 9, "8"), null_yield); |
7c673cae FG |
176 | ASSERT_FALSE(c9); |
177 | ASSERT_EQ(-EFAULT, c9.get_error()); | |
178 | ASSERT_EQ(Ids{"8"}, puller.ids); | |
179 | ||
b3b6e05e | 180 | auto c8 = history.attach(&dp, make_period("8", 8, "7"), null_yield); |
7c673cae FG |
181 | ASSERT_FALSE(c8); |
182 | ASSERT_EQ(-EFAULT, c8.get_error()); | |
183 | ASSERT_EQ(Ids({"8", "7"}), puller.ids); | |
184 | ||
b3b6e05e | 185 | auto c7 = history.attach(&dp, make_period("7", 7, "6"), null_yield); |
7c673cae FG |
186 | ASSERT_FALSE(c7); |
187 | ASSERT_EQ(-EFAULT, c7.get_error()); | |
188 | ASSERT_EQ(Ids({"8", "7", "6"}), puller.ids); | |
189 | ||
b3b6e05e | 190 | auto c6 = history.attach(&dp, make_period("6", 6, "5"), null_yield); |
7c673cae FG |
191 | ASSERT_TRUE(c6); |
192 | ASSERT_EQ(Ids({"8", "7", "6"}), puller.ids); | |
193 | } | |
194 | ||
195 | TEST(PeriodHistory, MergeBeforeCurrent) | |
196 | { | |
197 | RGWPeriodHistory history(g_ceph_context, &puller, current_period); | |
198 | ||
199 | auto c = history.get_current(); | |
200 | ASSERT_FALSE(c.has_prev()); | |
201 | ||
202 | // create a disjoint history at 3 | |
203 | auto c3 = history.insert(make_period("3", 3, "2")); | |
204 | ASSERT_FALSE(c3); | |
205 | ||
206 | // insert the missing period to merge 3 and 5 | |
207 | auto c4 = history.insert(make_period("4", 4, "3")); | |
208 | ASSERT_TRUE(c4); | |
209 | ASSERT_TRUE(c4.has_prev()); | |
210 | ASSERT_TRUE(c4.has_next()); | |
211 | ||
212 | // verify that the merge didn't destroy the original cursor's history | |
213 | ASSERT_EQ(current_period, c.get_period()); | |
214 | ASSERT_TRUE(c.has_prev()); | |
215 | } | |
216 | ||
217 | TEST(PeriodHistory, MergeAfterCurrent) | |
218 | { | |
219 | RGWPeriodHistory history(g_ceph_context, &puller, current_period); | |
220 | ||
221 | auto c = history.get_current(); | |
222 | ASSERT_FALSE(c.has_next()); | |
223 | ||
224 | // create a disjoint history at 7 | |
225 | auto c7 = history.insert(make_period("7", 7, "6")); | |
226 | ASSERT_FALSE(c7); | |
227 | ||
228 | // insert the missing period to merge 5 and 7 | |
229 | auto c6 = history.insert(make_period("6", 6, "5")); | |
230 | ASSERT_TRUE(c6); | |
231 | ASSERT_TRUE(c6.has_prev()); | |
232 | ASSERT_TRUE(c6.has_next()); | |
233 | ||
234 | // verify that the merge didn't destroy the original cursor's history | |
235 | ASSERT_EQ(current_period, c.get_period()); | |
236 | ASSERT_TRUE(c.has_next()); | |
237 | } | |
238 | ||
239 | TEST(PeriodHistory, MergeWithoutCurrent) | |
240 | { | |
241 | RGWPeriodHistory history(g_ceph_context, &puller, current_period); | |
242 | ||
243 | // create a disjoint history at 7 | |
244 | auto c7 = history.insert(make_period("7", 7, "6")); | |
245 | ASSERT_FALSE(c7); | |
246 | ||
247 | // create a disjoint history at 9 | |
248 | auto c9 = history.insert(make_period("9", 9, "8")); | |
249 | ASSERT_FALSE(c9); | |
250 | ||
251 | // insert the missing period to merge 7 and 9 | |
252 | auto c8 = history.insert(make_period("8", 8, "7")); | |
253 | ASSERT_FALSE(c8); // not connected to current_period yet | |
254 | ||
255 | // insert the missing period to merge 5 and 7-9 | |
256 | auto c = history.insert(make_period("6", 6, "5")); | |
257 | ASSERT_TRUE(c); | |
258 | ASSERT_TRUE(c.has_next()); | |
259 | ||
260 | // verify that we merged all periods from 5-9 | |
261 | c.next(); | |
262 | ASSERT_EQ(7u, c.get_epoch()); | |
263 | ASSERT_TRUE(c.has_next()); | |
264 | c.next(); | |
265 | ASSERT_EQ(8u, c.get_epoch()); | |
266 | ASSERT_TRUE(c.has_next()); | |
267 | c.next(); | |
268 | ASSERT_EQ(9u, c.get_epoch()); | |
269 | ASSERT_FALSE(c.has_next()); | |
270 | } | |
271 | ||
272 | TEST(PeriodHistory, AttachBefore) | |
273 | { | |
274 | NumericPuller puller; | |
275 | RGWPeriodHistory history(g_ceph_context, &puller, current_period); | |
b3b6e05e | 276 | const DoutPrefix dp(g_ceph_context, 1, "test rgw period history: "); |
7c673cae | 277 | |
b3b6e05e | 278 | auto c1 = history.attach(&dp, make_period("1", 1, ""), null_yield); |
7c673cae FG |
279 | ASSERT_TRUE(c1); |
280 | ||
281 | // verify that we pulled and merged all periods from 1-5 | |
282 | auto c = history.get_current(); | |
283 | ASSERT_TRUE(c); | |
284 | ASSERT_TRUE(c.has_prev()); | |
285 | c.prev(); | |
286 | ASSERT_EQ(4u, c.get_epoch()); | |
287 | ASSERT_TRUE(c.has_prev()); | |
288 | c.prev(); | |
289 | ASSERT_EQ(3u, c.get_epoch()); | |
290 | ASSERT_TRUE(c.has_prev()); | |
291 | c.prev(); | |
292 | ASSERT_EQ(2u, c.get_epoch()); | |
293 | ASSERT_TRUE(c.has_prev()); | |
294 | c.prev(); | |
295 | ASSERT_EQ(1u, c.get_epoch()); | |
296 | ASSERT_FALSE(c.has_prev()); | |
297 | } | |
298 | ||
299 | TEST(PeriodHistory, AttachAfter) | |
300 | { | |
301 | NumericPuller puller; | |
302 | RGWPeriodHistory history(g_ceph_context, &puller, current_period); | |
b3b6e05e | 303 | const DoutPrefix dp(g_ceph_context, 1, "test rgw period history: "); |
7c673cae | 304 | |
b3b6e05e | 305 | auto c9 = history.attach(&dp, make_period("9", 9, "8"), null_yield); |
7c673cae FG |
306 | ASSERT_TRUE(c9); |
307 | ||
308 | // verify that we pulled and merged all periods from 5-9 | |
309 | auto c = history.get_current(); | |
310 | ASSERT_TRUE(c); | |
311 | ASSERT_TRUE(c.has_next()); | |
312 | c.next(); | |
313 | ASSERT_EQ(6u, c.get_epoch()); | |
314 | ASSERT_TRUE(c.has_next()); | |
315 | c.next(); | |
316 | ASSERT_EQ(7u, c.get_epoch()); | |
317 | ASSERT_TRUE(c.has_next()); | |
318 | c.next(); | |
319 | ASSERT_EQ(8u, c.get_epoch()); | |
320 | ASSERT_TRUE(c.has_next()); | |
321 | c.next(); | |
322 | ASSERT_EQ(9u, c.get_epoch()); | |
323 | ASSERT_FALSE(c.has_next()); | |
324 | } | |
325 | ||
326 | int main(int argc, char** argv) | |
327 | { | |
20effc67 | 328 | auto args = argv_to_vec(argc, argv); |
7c673cae | 329 | auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, |
11fdf7f2 TL |
330 | CODE_ENVIRONMENT_UTILITY, |
331 | CINIT_FLAG_NO_DEFAULT_CONFIG_FILE); | |
7c673cae FG |
332 | common_init_finish(g_ceph_context); |
333 | ||
334 | ::testing::InitGoogleTest(&argc, argv); | |
335 | return RUN_ALL_TESTS(); | |
336 | } |