]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/rgw/test_rgw_period_history.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / test / rgw / test_rgw_period_history.cc
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"
16 #include "rgw/rgw_zone.h"
17 #include "global/global_init.h"
18 #include "common/ceph_argparse.h"
19 #include <boost/lexical_cast.hpp>
20 #include <gtest/gtest.h>
21
22 using namespace std;
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 {
39 int pull(const DoutPrefixProvider *dpp, const std::string& id, RGWPeriod& period, optional_yield) override {
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:
50 explicit RecordingPuller(int error) : error(error) {}
51 Ids ids;
52 int pull(const DoutPrefixProvider *dpp, const std::string& id, RGWPeriod& period, optional_yield) override {
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 {
60 int pull(const DoutPrefixProvider *dpp, const std::string& id, RGWPeriod& period, optional_yield) override {
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);
134 const DoutPrefix dp(g_ceph_context, 1, "test rgw period history: ");
135
136 // create a disjoint history at 1 and verify that periods are requested
137 // backwards from current_period
138 auto c1 = history.attach(&dp, make_period("1", 1, ""), null_yield);
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
146 c1 = history.attach(&dp, make_period("1", 1, ""), null_yield);
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
154 c1 = history.attach(&dp, make_period("1", 1, ""), null_yield);
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
162 c1 = history.attach(&dp, make_period("1", 1, ""), null_yield);
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);
171 const DoutPrefix dp(g_ceph_context, 1, "test rgw period history: ");
172
173 // create a disjoint history at 9 and verify that periods are requested
174 // backwards down to current_period
175 auto c9 = history.attach(&dp, make_period("9", 9, "8"), null_yield);
176 ASSERT_FALSE(c9);
177 ASSERT_EQ(-EFAULT, c9.get_error());
178 ASSERT_EQ(Ids{"8"}, puller.ids);
179
180 auto c8 = history.attach(&dp, make_period("8", 8, "7"), null_yield);
181 ASSERT_FALSE(c8);
182 ASSERT_EQ(-EFAULT, c8.get_error());
183 ASSERT_EQ(Ids({"8", "7"}), puller.ids);
184
185 auto c7 = history.attach(&dp, make_period("7", 7, "6"), null_yield);
186 ASSERT_FALSE(c7);
187 ASSERT_EQ(-EFAULT, c7.get_error());
188 ASSERT_EQ(Ids({"8", "7", "6"}), puller.ids);
189
190 auto c6 = history.attach(&dp, make_period("6", 6, "5"), null_yield);
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);
276 const DoutPrefix dp(g_ceph_context, 1, "test rgw period history: ");
277
278 auto c1 = history.attach(&dp, make_period("1", 1, ""), null_yield);
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);
303 const DoutPrefix dp(g_ceph_context, 1, "test rgw period history: ");
304
305 auto c9 = history.attach(&dp, make_period("9", 9, "8"), null_yield);
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 {
328 auto args = argv_to_vec(argc, argv);
329 auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
330 CODE_ENVIRONMENT_UTILITY,
331 CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
332 common_init_finish(g_ceph_context);
333
334 ::testing::InitGoogleTest(&argc, argv);
335 return RUN_ALL_TESTS();
336 }