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_period_history.h"
15 #include "rgw_rados.h"
17 #include "global/global_init.h"
18 #include "common/ceph_argparse.h"
19 #include <boost/lexical_cast.hpp>
20 #include <gtest/gtest.h>
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
)
30 period
.set_realm_epoch(realm_epoch
);
31 period
.set_predecessor(predecessor
);
35 const auto current_period
= make_period("5", 5, "4");
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");
43 ErrorPuller puller
; // default puller
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
{
50 explicit RecordingPuller(int error
) : error(error
) {}
52 int pull(const DoutPrefixProvider
*dpp
, const std::string
& id
, RGWPeriod
& period
, optional_yield
) override
{
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
);
69 } // anonymous namespace
72 bool operator==(const RGWPeriod
& lhs
, const RGWPeriod
& rhs
)
74 return lhs
.get_id() == rhs
.get_id()
75 && lhs
.get_realm_epoch() == rhs
.get_realm_epoch();
78 TEST(PeriodHistory
, InsertBefore
)
80 RGWPeriodHistory
history(g_ceph_context
, &puller
, current_period
);
82 // inserting right before current_period 5 will attach to history
83 auto c
= history
.insert(make_period("4", 4, "3"));
85 ASSERT_FALSE(c
.has_prev());
86 ASSERT_TRUE(c
.has_next());
88 // cursor can traverse forward to current_period
90 ASSERT_EQ(5u, c
.get_epoch());
91 ASSERT_EQ(current_period
, c
.get_period());
94 TEST(PeriodHistory
, InsertAfter
)
96 RGWPeriodHistory
history(g_ceph_context
, &puller
, current_period
);
98 // inserting right after current_period 5 will attach to history
99 auto c
= history
.insert(make_period("6", 6, "5"));
101 ASSERT_TRUE(c
.has_prev());
102 ASSERT_FALSE(c
.has_next());
104 // cursor can traverse back to current_period
106 ASSERT_EQ(5u, c
.get_epoch());
107 ASSERT_EQ(current_period
, c
.get_period());
110 TEST(PeriodHistory
, InsertWayBefore
)
112 RGWPeriodHistory
history(g_ceph_context
, &puller
, current_period
);
114 // inserting way before current_period 5 will not attach to history
115 auto c
= history
.insert(make_period("1", 1, ""));
117 ASSERT_EQ(0, c
.get_error());
120 TEST(PeriodHistory
, InsertWayAfter
)
122 RGWPeriodHistory
history(g_ceph_context
, &puller
, current_period
);
124 // inserting way after current_period 5 will not attach to history
125 auto c
= history
.insert(make_period("9", 9, "8"));
127 ASSERT_EQ(0, c
.get_error());
130 TEST(PeriodHistory
, PullPredecessorsBeforeCurrent
)
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: ");
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
);
140 ASSERT_EQ(-EFAULT
, c1
.get_error());
141 ASSERT_EQ(Ids
{"4"}, puller
.ids
);
143 auto c4
= history
.insert(make_period("4", 4, "3"));
146 c1
= history
.attach(&dp
, make_period("1", 1, ""), null_yield
);
148 ASSERT_EQ(-EFAULT
, c1
.get_error());
149 ASSERT_EQ(Ids({"4", "3"}), puller
.ids
);
151 auto c3
= history
.insert(make_period("3", 3, "2"));
154 c1
= history
.attach(&dp
, make_period("1", 1, ""), null_yield
);
156 ASSERT_EQ(-EFAULT
, c1
.get_error());
157 ASSERT_EQ(Ids({"4", "3", "2"}), puller
.ids
);
159 auto c2
= history
.insert(make_period("2", 2, "1"));
162 c1
= history
.attach(&dp
, make_period("1", 1, ""), null_yield
);
164 ASSERT_EQ(Ids({"4", "3", "2"}), puller
.ids
);
167 TEST(PeriodHistory
, PullPredecessorsAfterCurrent
)
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: ");
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
);
177 ASSERT_EQ(-EFAULT
, c9
.get_error());
178 ASSERT_EQ(Ids
{"8"}, puller
.ids
);
180 auto c8
= history
.attach(&dp
, make_period("8", 8, "7"), null_yield
);
182 ASSERT_EQ(-EFAULT
, c8
.get_error());
183 ASSERT_EQ(Ids({"8", "7"}), puller
.ids
);
185 auto c7
= history
.attach(&dp
, make_period("7", 7, "6"), null_yield
);
187 ASSERT_EQ(-EFAULT
, c7
.get_error());
188 ASSERT_EQ(Ids({"8", "7", "6"}), puller
.ids
);
190 auto c6
= history
.attach(&dp
, make_period("6", 6, "5"), null_yield
);
192 ASSERT_EQ(Ids({"8", "7", "6"}), puller
.ids
);
195 TEST(PeriodHistory
, MergeBeforeCurrent
)
197 RGWPeriodHistory
history(g_ceph_context
, &puller
, current_period
);
199 auto c
= history
.get_current();
200 ASSERT_FALSE(c
.has_prev());
202 // create a disjoint history at 3
203 auto c3
= history
.insert(make_period("3", 3, "2"));
206 // insert the missing period to merge 3 and 5
207 auto c4
= history
.insert(make_period("4", 4, "3"));
209 ASSERT_TRUE(c4
.has_prev());
210 ASSERT_TRUE(c4
.has_next());
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());
217 TEST(PeriodHistory
, MergeAfterCurrent
)
219 RGWPeriodHistory
history(g_ceph_context
, &puller
, current_period
);
221 auto c
= history
.get_current();
222 ASSERT_FALSE(c
.has_next());
224 // create a disjoint history at 7
225 auto c7
= history
.insert(make_period("7", 7, "6"));
228 // insert the missing period to merge 5 and 7
229 auto c6
= history
.insert(make_period("6", 6, "5"));
231 ASSERT_TRUE(c6
.has_prev());
232 ASSERT_TRUE(c6
.has_next());
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());
239 TEST(PeriodHistory
, MergeWithoutCurrent
)
241 RGWPeriodHistory
history(g_ceph_context
, &puller
, current_period
);
243 // create a disjoint history at 7
244 auto c7
= history
.insert(make_period("7", 7, "6"));
247 // create a disjoint history at 9
248 auto c9
= history
.insert(make_period("9", 9, "8"));
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
255 // insert the missing period to merge 5 and 7-9
256 auto c
= history
.insert(make_period("6", 6, "5"));
258 ASSERT_TRUE(c
.has_next());
260 // verify that we merged all periods from 5-9
262 ASSERT_EQ(7u, c
.get_epoch());
263 ASSERT_TRUE(c
.has_next());
265 ASSERT_EQ(8u, c
.get_epoch());
266 ASSERT_TRUE(c
.has_next());
268 ASSERT_EQ(9u, c
.get_epoch());
269 ASSERT_FALSE(c
.has_next());
272 TEST(PeriodHistory
, AttachBefore
)
274 NumericPuller puller
;
275 RGWPeriodHistory
history(g_ceph_context
, &puller
, current_period
);
276 const DoutPrefix
dp(g_ceph_context
, 1, "test rgw period history: ");
278 auto c1
= history
.attach(&dp
, make_period("1", 1, ""), null_yield
);
281 // verify that we pulled and merged all periods from 1-5
282 auto c
= history
.get_current();
284 ASSERT_TRUE(c
.has_prev());
286 ASSERT_EQ(4u, c
.get_epoch());
287 ASSERT_TRUE(c
.has_prev());
289 ASSERT_EQ(3u, c
.get_epoch());
290 ASSERT_TRUE(c
.has_prev());
292 ASSERT_EQ(2u, c
.get_epoch());
293 ASSERT_TRUE(c
.has_prev());
295 ASSERT_EQ(1u, c
.get_epoch());
296 ASSERT_FALSE(c
.has_prev());
299 TEST(PeriodHistory
, AttachAfter
)
301 NumericPuller puller
;
302 RGWPeriodHistory
history(g_ceph_context
, &puller
, current_period
);
303 const DoutPrefix
dp(g_ceph_context
, 1, "test rgw period history: ");
305 auto c9
= history
.attach(&dp
, make_period("9", 9, "8"), null_yield
);
308 // verify that we pulled and merged all periods from 5-9
309 auto c
= history
.get_current();
311 ASSERT_TRUE(c
.has_next());
313 ASSERT_EQ(6u, c
.get_epoch());
314 ASSERT_TRUE(c
.has_next());
316 ASSERT_EQ(7u, c
.get_epoch());
317 ASSERT_TRUE(c
.has_next());
319 ASSERT_EQ(8u, c
.get_epoch());
320 ASSERT_TRUE(c
.has_next());
322 ASSERT_EQ(9u, c
.get_epoch());
323 ASSERT_FALSE(c
.has_next());
326 int main(int argc
, char** argv
)
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
);
334 ::testing::InitGoogleTest(&argc
, argv
);
335 return RUN_ALL_TESTS();