1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2015 Red Hat
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.
14 #include "rgw/rgw_period_history.h"
15 #include "rgw/rgw_rados.h"
16 #include "global/global_init.h"
17 #include "common/ceph_argparse.h"
18 #include <boost/lexical_cast.hpp>
19 #include <gtest/gtest.h>
23 // construct a period with the given fields
24 RGWPeriod make_period(const std::string& id, epoch_t realm_epoch,
25 const std::string& predecessor)
28 period.set_realm_epoch(realm_epoch);
29 period.set_predecessor(predecessor);
33 const auto current_period = make_period("5", 5, "4");
35 // mock puller that throws an exception if it's called
36 struct ErrorPuller : public RGWPeriodHistory::Puller {
37 int pull(const std::string& id, RGWPeriod& period) override {
38 throw std::runtime_error("unexpected call to pull");
41 ErrorPuller puller; // default puller
43 // mock puller that records the period ids requested and returns an error
44 using Ids = std::vector<std::string>;
45 class RecordingPuller : public RGWPeriodHistory::Puller {
48 RecordingPuller(int error) : error(error) {}
50 int pull(const std::string& id, RGWPeriod& period) override {
56 // mock puller that returns a fake period by parsing the period id
57 struct NumericPuller : public RGWPeriodHistory::Puller {
58 int pull(const std::string& id, RGWPeriod& period) override {
59 // relies on numeric period ids to divine the realm_epoch
60 auto realm_epoch = boost::lexical_cast<epoch_t>(id);
61 auto predecessor = boost::lexical_cast<std::string>(realm_epoch-1);
62 period = make_period(id, realm_epoch, predecessor);
67 } // anonymous namespace
70 bool operator==(const RGWPeriod& lhs, const RGWPeriod& rhs)
72 return lhs.get_id() == rhs.get_id()
73 && lhs.get_realm_epoch() == rhs.get_realm_epoch();
76 TEST(PeriodHistory, InsertBefore)
78 RGWPeriodHistory history(g_ceph_context, &puller, current_period);
80 // inserting right before current_period 5 will attach to history
81 auto c = history.insert(make_period("4", 4, "3"));
83 ASSERT_FALSE(c.has_prev());
84 ASSERT_TRUE(c.has_next());
86 // cursor can traverse forward to current_period
88 ASSERT_EQ(5u, c.get_epoch());
89 ASSERT_EQ(current_period, c.get_period());
92 TEST(PeriodHistory, InsertAfter)
94 RGWPeriodHistory history(g_ceph_context, &puller, current_period);
96 // inserting right after current_period 5 will attach to history
97 auto c = history.insert(make_period("6", 6, "5"));
99 ASSERT_TRUE(c.has_prev());
100 ASSERT_FALSE(c.has_next());
102 // cursor can traverse back to current_period
104 ASSERT_EQ(5u, c.get_epoch());
105 ASSERT_EQ(current_period, c.get_period());
108 TEST(PeriodHistory, InsertWayBefore)
110 RGWPeriodHistory history(g_ceph_context, &puller, current_period);
112 // inserting way before current_period 5 will not attach to history
113 auto c = history.insert(make_period("1", 1, ""));
115 ASSERT_EQ(0, c.get_error());
118 TEST(PeriodHistory, InsertWayAfter)
120 RGWPeriodHistory history(g_ceph_context, &puller, current_period);
122 // inserting way after current_period 5 will not attach to history
123 auto c = history.insert(make_period("9", 9, "8"));
125 ASSERT_EQ(0, c.get_error());
128 TEST(PeriodHistory, PullPredecessorsBeforeCurrent)
130 RecordingPuller puller{-EFAULT};
131 RGWPeriodHistory history(g_ceph_context, &puller, current_period);
133 // create a disjoint history at 1 and verify that periods are requested
134 // backwards from current_period
135 auto c1 = history.attach(make_period("1", 1, ""));
137 ASSERT_EQ(-EFAULT, c1.get_error());
138 ASSERT_EQ(Ids{"4"}, puller.ids);
140 auto c4 = history.insert(make_period("4", 4, "3"));
143 c1 = history.attach(make_period("1", 1, ""));
145 ASSERT_EQ(-EFAULT, c1.get_error());
146 ASSERT_EQ(Ids({"4", "3"}), puller.ids);
148 auto c3 = history.insert(make_period("3", 3, "2"));
151 c1 = history.attach(make_period("1", 1, ""));
153 ASSERT_EQ(-EFAULT, c1.get_error());
154 ASSERT_EQ(Ids({"4", "3", "2"}), puller.ids);
156 auto c2 = history.insert(make_period("2", 2, "1"));
159 c1 = history.attach(make_period("1", 1, ""));
161 ASSERT_EQ(Ids({"4", "3", "2"}), puller.ids);
164 TEST(PeriodHistory, PullPredecessorsAfterCurrent)
166 RecordingPuller puller{-EFAULT};
167 RGWPeriodHistory history(g_ceph_context, &puller, current_period);
169 // create a disjoint history at 9 and verify that periods are requested
170 // backwards down to current_period
171 auto c9 = history.attach(make_period("9", 9, "8"));
173 ASSERT_EQ(-EFAULT, c9.get_error());
174 ASSERT_EQ(Ids{"8"}, puller.ids);
176 auto c8 = history.attach(make_period("8", 8, "7"));
178 ASSERT_EQ(-EFAULT, c8.get_error());
179 ASSERT_EQ(Ids({"8", "7"}), puller.ids);
181 auto c7 = history.attach(make_period("7", 7, "6"));
183 ASSERT_EQ(-EFAULT, c7.get_error());
184 ASSERT_EQ(Ids({"8", "7", "6"}), puller.ids);
186 auto c6 = history.attach(make_period("6", 6, "5"));
188 ASSERT_EQ(Ids({"8", "7", "6"}), puller.ids);
191 TEST(PeriodHistory, MergeBeforeCurrent)
193 RGWPeriodHistory history(g_ceph_context, &puller, current_period);
195 auto c = history.get_current();
196 ASSERT_FALSE(c.has_prev());
198 // create a disjoint history at 3
199 auto c3 = history.insert(make_period("3", 3, "2"));
202 // insert the missing period to merge 3 and 5
203 auto c4 = history.insert(make_period("4", 4, "3"));
205 ASSERT_TRUE(c4.has_prev());
206 ASSERT_TRUE(c4.has_next());
208 // verify that the merge didn't destroy the original cursor's history
209 ASSERT_EQ(current_period, c.get_period());
210 ASSERT_TRUE(c.has_prev());
213 TEST(PeriodHistory, MergeAfterCurrent)
215 RGWPeriodHistory history(g_ceph_context, &puller, current_period);
217 auto c = history.get_current();
218 ASSERT_FALSE(c.has_next());
220 // create a disjoint history at 7
221 auto c7 = history.insert(make_period("7", 7, "6"));
224 // insert the missing period to merge 5 and 7
225 auto c6 = history.insert(make_period("6", 6, "5"));
227 ASSERT_TRUE(c6.has_prev());
228 ASSERT_TRUE(c6.has_next());
230 // verify that the merge didn't destroy the original cursor's history
231 ASSERT_EQ(current_period, c.get_period());
232 ASSERT_TRUE(c.has_next());
235 TEST(PeriodHistory, MergeWithoutCurrent)
237 RGWPeriodHistory history(g_ceph_context, &puller, current_period);
239 // create a disjoint history at 7
240 auto c7 = history.insert(make_period("7", 7, "6"));
243 // create a disjoint history at 9
244 auto c9 = history.insert(make_period("9", 9, "8"));
247 // insert the missing period to merge 7 and 9
248 auto c8 = history.insert(make_period("8", 8, "7"));
249 ASSERT_FALSE(c8); // not connected to current_period yet
251 // insert the missing period to merge 5 and 7-9
252 auto c = history.insert(make_period("6", 6, "5"));
254 ASSERT_TRUE(c.has_next());
256 // verify that we merged all periods from 5-9
258 ASSERT_EQ(7u, c.get_epoch());
259 ASSERT_TRUE(c.has_next());
261 ASSERT_EQ(8u, c.get_epoch());
262 ASSERT_TRUE(c.has_next());
264 ASSERT_EQ(9u, c.get_epoch());
265 ASSERT_FALSE(c.has_next());
268 TEST(PeriodHistory, AttachBefore)
270 NumericPuller puller;
271 RGWPeriodHistory history(g_ceph_context, &puller, current_period);
273 auto c1 = history.attach(make_period("1", 1, ""));
276 // verify that we pulled and merged all periods from 1-5
277 auto c = history.get_current();
279 ASSERT_TRUE(c.has_prev());
281 ASSERT_EQ(4u, c.get_epoch());
282 ASSERT_TRUE(c.has_prev());
284 ASSERT_EQ(3u, c.get_epoch());
285 ASSERT_TRUE(c.has_prev());
287 ASSERT_EQ(2u, c.get_epoch());
288 ASSERT_TRUE(c.has_prev());
290 ASSERT_EQ(1u, c.get_epoch());
291 ASSERT_FALSE(c.has_prev());
294 TEST(PeriodHistory, AttachAfter)
296 NumericPuller puller;
297 RGWPeriodHistory history(g_ceph_context, &puller, current_period);
299 auto c9 = history.attach(make_period("9", 9, "8"));
302 // verify that we pulled and merged all periods from 5-9
303 auto c = history.get_current();
305 ASSERT_TRUE(c.has_next());
307 ASSERT_EQ(6u, c.get_epoch());
308 ASSERT_TRUE(c.has_next());
310 ASSERT_EQ(7u, c.get_epoch());
311 ASSERT_TRUE(c.has_next());
313 ASSERT_EQ(8u, c.get_epoch());
314 ASSERT_TRUE(c.has_next());
316 ASSERT_EQ(9u, c.get_epoch());
317 ASSERT_FALSE(c.has_next());
320 int main(int argc, char** argv)
322 vector<const char*> args;
323 argv_to_vec(argc, (const char **)argv, args);
325 auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
326 CODE_ENVIRONMENT_UTILITY, 0);
327 common_init_finish(g_ceph_context);
329 ::testing::InitGoogleTest(&argc, argv);
330 return RUN_ALL_TESTS();