From e6918187568dbd01842d8d1d2c808ce16a894239 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 21 Apr 2024 13:54:28 +0200 Subject: Adding upstream version 18.2.2. Signed-off-by: Daniel Baumann --- src/rgw/driver/rados/config/period.cc | 230 ++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 src/rgw/driver/rados/config/period.cc (limited to 'src/rgw/driver/rados/config/period.cc') diff --git a/src/rgw/driver/rados/config/period.cc b/src/rgw/driver/rados/config/period.cc new file mode 100644 index 000000000..bc3fa27e7 --- /dev/null +++ b/src/rgw/driver/rados/config/period.cc @@ -0,0 +1,230 @@ +// vim: ts=8 sw=2 smarttab ft=cpp + +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2022 Red Hat, Inc. + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "common/dout.h" +#include "common/errno.h" +#include "rgw_zone.h" +#include "driver/rados/config/store.h" + +#include "impl.h" + +namespace rgw::rados { + +// period oids +constexpr std::string_view period_info_oid_prefix = "periods."; +constexpr std::string_view period_latest_epoch_info_oid = ".latest_epoch"; +constexpr std::string_view period_staging_suffix = ":staging"; + +static std::string period_oid(std::string_view period_id, uint32_t epoch) +{ + // omit the epoch for the staging period + if (period_id.ends_with(period_staging_suffix)) { + return string_cat_reserve(period_info_oid_prefix, period_id); + } + return fmt::format("{}{}.{}", period_info_oid_prefix, period_id, epoch); +} + +static std::string latest_epoch_oid(const ceph::common::ConfigProxy& conf, + std::string_view period_id) +{ + return string_cat_reserve( + period_info_oid_prefix, period_id, + name_or_default(conf->rgw_period_latest_epoch_info_oid, + period_latest_epoch_info_oid)); +} + +static int read_latest_epoch(const DoutPrefixProvider* dpp, optional_yield y, + ConfigImpl* impl, std::string_view period_id, + uint32_t& epoch, RGWObjVersionTracker* objv) +{ + const auto& pool = impl->period_pool; + const auto latest_oid = latest_epoch_oid(dpp->get_cct()->_conf, period_id); + RGWPeriodLatestEpochInfo latest; + int r = impl->read(dpp, y, pool, latest_oid, latest, objv); + if (r >= 0) { + epoch = latest.epoch; + } + return r; +} + +static int write_latest_epoch(const DoutPrefixProvider* dpp, optional_yield y, + ConfigImpl* impl, bool exclusive, + std::string_view period_id, uint32_t epoch, + RGWObjVersionTracker* objv) +{ + const auto& pool = impl->period_pool; + const auto latest_oid = latest_epoch_oid(dpp->get_cct()->_conf, period_id); + const auto create = exclusive ? Create::MustNotExist : Create::MayExist; + RGWPeriodLatestEpochInfo latest{epoch}; + return impl->write(dpp, y, pool, latest_oid, create, latest, objv); +} + +static int delete_latest_epoch(const DoutPrefixProvider* dpp, optional_yield y, + ConfigImpl* impl, std::string_view period_id, + RGWObjVersionTracker* objv) +{ + const auto& pool = impl->period_pool; + const auto latest_oid = latest_epoch_oid(dpp->get_cct()->_conf, period_id); + return impl->remove(dpp, y, pool, latest_oid, objv); +} + +static int update_latest_epoch(const DoutPrefixProvider* dpp, optional_yield y, + ConfigImpl* impl, std::string_view period_id, + uint32_t epoch) +{ + static constexpr int MAX_RETRIES = 20; + + for (int i = 0; i < MAX_RETRIES; i++) { + uint32_t existing_epoch = 0; + RGWObjVersionTracker objv; + bool exclusive = false; + + // read existing epoch + int r = read_latest_epoch(dpp, y, impl, period_id, existing_epoch, &objv); + if (r == -ENOENT) { + // use an exclusive create to set the epoch atomically + exclusive = true; + objv.generate_new_write_ver(dpp->get_cct()); + ldpp_dout(dpp, 20) << "creating initial latest_epoch=" << epoch + << " for period=" << period_id << dendl; + } else if (r < 0) { + ldpp_dout(dpp, 0) << "ERROR: failed to read latest_epoch" << dendl; + return r; + } else if (epoch <= existing_epoch) { + r = -EEXIST; // fail with EEXIST if epoch is not newer + ldpp_dout(dpp, 10) << "found existing latest_epoch " << existing_epoch + << " >= given epoch " << epoch << ", returning r=" << r << dendl; + return r; + } else { + ldpp_dout(dpp, 20) << "updating latest_epoch from " << existing_epoch + << " -> " << epoch << " on period=" << period_id << dendl; + } + + r = write_latest_epoch(dpp, y, impl, exclusive, period_id, epoch, &objv); + if (r == -EEXIST) { + continue; // exclusive create raced with another update, retry + } else if (r == -ECANCELED) { + continue; // write raced with a conflicting version, retry + } + if (r < 0) { + ldpp_dout(dpp, 0) << "ERROR: failed to write latest_epoch" << dendl; + return r; + } + return 0; // return success + } + + return -ECANCELED; // fail after max retries +} + +int RadosConfigStore::create_period(const DoutPrefixProvider* dpp, + optional_yield y, bool exclusive, + const RGWPeriod& info) +{ + if (info.get_id().empty()) { + ldpp_dout(dpp, 0) << "period cannot have an empty id" << dendl; + return -EINVAL; + } + if (info.get_epoch() == 0) { + ldpp_dout(dpp, 0) << "period cannot have an empty epoch" << dendl; + return -EINVAL; + } + const auto& pool = impl->period_pool; + const auto info_oid = period_oid(info.get_id(), info.get_epoch()); + const auto create = exclusive ? Create::MustNotExist : Create::MayExist; + RGWObjVersionTracker objv; + objv.generate_new_write_ver(dpp->get_cct()); + int r = impl->write(dpp, y, pool, info_oid, create, info, &objv); + if (r < 0) { + return r; + } + + (void) update_latest_epoch(dpp, y, impl.get(), info.get_id(), info.get_epoch()); + return 0; +} + +int RadosConfigStore::read_period(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view period_id, + std::optional epoch, + RGWPeriod& info) +{ + int r = 0; + if (!epoch) { + epoch = 0; + r = read_latest_epoch(dpp, y, impl.get(), period_id, *epoch, nullptr); + if (r < 0) { + return r; + } + } + + const auto& pool = impl->period_pool; + const auto info_oid = period_oid(period_id, *epoch); + return impl->read(dpp, y, pool, info_oid, info, nullptr); +} + +int RadosConfigStore::delete_period(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view period_id) +{ + const auto& pool = impl->period_pool; + + // read the latest_epoch + uint32_t latest_epoch = 0; + RGWObjVersionTracker latest_objv; + int r = read_latest_epoch(dpp, y, impl.get(), period_id, + latest_epoch, &latest_objv); + if (r < 0 && r != -ENOENT) { // just delete epoch=0 on ENOENT + ldpp_dout(dpp, 0) << "failed to read latest epoch for period " + << period_id << ": " << cpp_strerror(r) << dendl; + return r; + } + + for (uint32_t epoch = 0; epoch <= latest_epoch; epoch++) { + const auto info_oid = period_oid(period_id, epoch); + r = impl->remove(dpp, y, pool, info_oid, nullptr); + if (r < 0 && r != -ENOENT) { // ignore ENOENT + ldpp_dout(dpp, 0) << "failed to delete period " << info_oid + << ": " << cpp_strerror(r) << dendl; + return r; + } + } + + return delete_latest_epoch(dpp, y, impl.get(), period_id, &latest_objv); +} + +int RadosConfigStore::list_period_ids(const DoutPrefixProvider* dpp, + optional_yield y, + const std::string& marker, + std::span entries, + sal::ListResult& result) +{ + const auto& pool = impl->period_pool; + constexpr auto prefix = [] (std::string oid) -> std::string { + if (!oid.starts_with(period_info_oid_prefix)) { + return {}; + } + if (!oid.ends_with(period_latest_epoch_info_oid)) { + return {}; + } + // trim the prefix and suffix + const std::size_t count = oid.size() - + period_info_oid_prefix.size() - + period_latest_epoch_info_oid.size(); + return oid.substr(period_info_oid_prefix.size(), count); + }; + + return impl->list(dpp, y, pool, marker, prefix, entries, result); +} + +} // namespace rgw::rados -- cgit v1.2.3