1 // vim: ts=8 sw=2 smarttab ft=cpp
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2022 Red Hat, Inc.
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.
15 #include "common/dout.h"
16 #include "common/errno.h"
18 #include "driver/rados/config/store.h"
22 namespace rgw::rados
{
25 constexpr std::string_view period_info_oid_prefix
= "periods.";
26 constexpr std::string_view period_latest_epoch_info_oid
= ".latest_epoch";
27 constexpr std::string_view period_staging_suffix
= ":staging";
29 static std::string
period_oid(std::string_view period_id
, uint32_t epoch
)
31 // omit the epoch for the staging period
32 if (period_id
.ends_with(period_staging_suffix
)) {
33 return string_cat_reserve(period_info_oid_prefix
, period_id
);
35 return fmt::format("{}{}.{}", period_info_oid_prefix
, period_id
, epoch
);
38 static std::string
latest_epoch_oid(const ceph::common::ConfigProxy
& conf
,
39 std::string_view period_id
)
41 return string_cat_reserve(
42 period_info_oid_prefix
, period_id
,
43 name_or_default(conf
->rgw_period_latest_epoch_info_oid
,
44 period_latest_epoch_info_oid
));
47 static int read_latest_epoch(const DoutPrefixProvider
* dpp
, optional_yield y
,
48 ConfigImpl
* impl
, std::string_view period_id
,
49 uint32_t& epoch
, RGWObjVersionTracker
* objv
)
51 const auto& pool
= impl
->period_pool
;
52 const auto latest_oid
= latest_epoch_oid(dpp
->get_cct()->_conf
, period_id
);
53 RGWPeriodLatestEpochInfo latest
;
54 int r
= impl
->read(dpp
, y
, pool
, latest_oid
, latest
, objv
);
61 static int write_latest_epoch(const DoutPrefixProvider
* dpp
, optional_yield y
,
62 ConfigImpl
* impl
, bool exclusive
,
63 std::string_view period_id
, uint32_t epoch
,
64 RGWObjVersionTracker
* objv
)
66 const auto& pool
= impl
->period_pool
;
67 const auto latest_oid
= latest_epoch_oid(dpp
->get_cct()->_conf
, period_id
);
68 const auto create
= exclusive
? Create::MustNotExist
: Create::MayExist
;
69 RGWPeriodLatestEpochInfo latest
{epoch
};
70 return impl
->write(dpp
, y
, pool
, latest_oid
, create
, latest
, objv
);
73 static int delete_latest_epoch(const DoutPrefixProvider
* dpp
, optional_yield y
,
74 ConfigImpl
* impl
, std::string_view period_id
,
75 RGWObjVersionTracker
* objv
)
77 const auto& pool
= impl
->period_pool
;
78 const auto latest_oid
= latest_epoch_oid(dpp
->get_cct()->_conf
, period_id
);
79 return impl
->remove(dpp
, y
, pool
, latest_oid
, objv
);
82 static int update_latest_epoch(const DoutPrefixProvider
* dpp
, optional_yield y
,
83 ConfigImpl
* impl
, std::string_view period_id
,
86 static constexpr int MAX_RETRIES
= 20;
88 for (int i
= 0; i
< MAX_RETRIES
; i
++) {
89 uint32_t existing_epoch
= 0;
90 RGWObjVersionTracker objv
;
91 bool exclusive
= false;
93 // read existing epoch
94 int r
= read_latest_epoch(dpp
, y
, impl
, period_id
, existing_epoch
, &objv
);
96 // use an exclusive create to set the epoch atomically
98 objv
.generate_new_write_ver(dpp
->get_cct());
99 ldpp_dout(dpp
, 20) << "creating initial latest_epoch=" << epoch
100 << " for period=" << period_id
<< dendl
;
102 ldpp_dout(dpp
, 0) << "ERROR: failed to read latest_epoch" << dendl
;
104 } else if (epoch
<= existing_epoch
) {
105 r
= -EEXIST
; // fail with EEXIST if epoch is not newer
106 ldpp_dout(dpp
, 10) << "found existing latest_epoch " << existing_epoch
107 << " >= given epoch " << epoch
<< ", returning r=" << r
<< dendl
;
110 ldpp_dout(dpp
, 20) << "updating latest_epoch from " << existing_epoch
111 << " -> " << epoch
<< " on period=" << period_id
<< dendl
;
114 r
= write_latest_epoch(dpp
, y
, impl
, exclusive
, period_id
, epoch
, &objv
);
116 continue; // exclusive create raced with another update, retry
117 } else if (r
== -ECANCELED
) {
118 continue; // write raced with a conflicting version, retry
121 ldpp_dout(dpp
, 0) << "ERROR: failed to write latest_epoch" << dendl
;
124 return 0; // return success
127 return -ECANCELED
; // fail after max retries
130 int RadosConfigStore::create_period(const DoutPrefixProvider
* dpp
,
131 optional_yield y
, bool exclusive
,
132 const RGWPeriod
& info
)
134 if (info
.get_id().empty()) {
135 ldpp_dout(dpp
, 0) << "period cannot have an empty id" << dendl
;
138 if (info
.get_epoch() == 0) {
139 ldpp_dout(dpp
, 0) << "period cannot have an empty epoch" << dendl
;
142 const auto& pool
= impl
->period_pool
;
143 const auto info_oid
= period_oid(info
.get_id(), info
.get_epoch());
144 const auto create
= exclusive
? Create::MustNotExist
: Create::MayExist
;
145 RGWObjVersionTracker objv
;
146 objv
.generate_new_write_ver(dpp
->get_cct());
147 int r
= impl
->write(dpp
, y
, pool
, info_oid
, create
, info
, &objv
);
152 (void) update_latest_epoch(dpp
, y
, impl
.get(), info
.get_id(), info
.get_epoch());
156 int RadosConfigStore::read_period(const DoutPrefixProvider
* dpp
,
158 std::string_view period_id
,
159 std::optional
<uint32_t> epoch
,
165 r
= read_latest_epoch(dpp
, y
, impl
.get(), period_id
, *epoch
, nullptr);
171 const auto& pool
= impl
->period_pool
;
172 const auto info_oid
= period_oid(period_id
, *epoch
);
173 return impl
->read(dpp
, y
, pool
, info_oid
, info
, nullptr);
176 int RadosConfigStore::delete_period(const DoutPrefixProvider
* dpp
,
178 std::string_view period_id
)
180 const auto& pool
= impl
->period_pool
;
182 // read the latest_epoch
183 uint32_t latest_epoch
= 0;
184 RGWObjVersionTracker latest_objv
;
185 int r
= read_latest_epoch(dpp
, y
, impl
.get(), period_id
,
186 latest_epoch
, &latest_objv
);
187 if (r
< 0 && r
!= -ENOENT
) { // just delete epoch=0 on ENOENT
188 ldpp_dout(dpp
, 0) << "failed to read latest epoch for period "
189 << period_id
<< ": " << cpp_strerror(r
) << dendl
;
193 for (uint32_t epoch
= 0; epoch
<= latest_epoch
; epoch
++) {
194 const auto info_oid
= period_oid(period_id
, epoch
);
195 r
= impl
->remove(dpp
, y
, pool
, info_oid
, nullptr);
196 if (r
< 0 && r
!= -ENOENT
) { // ignore ENOENT
197 ldpp_dout(dpp
, 0) << "failed to delete period " << info_oid
198 << ": " << cpp_strerror(r
) << dendl
;
203 return delete_latest_epoch(dpp
, y
, impl
.get(), period_id
, &latest_objv
);
206 int RadosConfigStore::list_period_ids(const DoutPrefixProvider
* dpp
,
208 const std::string
& marker
,
209 std::span
<std::string
> entries
,
210 sal::ListResult
<std::string
>& result
)
212 const auto& pool
= impl
->period_pool
;
213 constexpr auto prefix
= [] (std::string oid
) -> std::string
{
214 if (!oid
.starts_with(period_info_oid_prefix
)) {
217 if (!oid
.ends_with(period_latest_epoch_info_oid
)) {
220 // trim the prefix and suffix
221 const std::size_t count
= oid
.size() -
222 period_info_oid_prefix
.size() -
223 period_latest_epoch_info_oid
.size();
224 return oid
.substr(period_info_oid_prefix
.size(), count
);
227 return impl
->list(dpp
, y
, pool
, marker
, prefix
, entries
, result
);
230 } // namespace rgw::rados