rgw: Implement support for --shard-id in radosgw-admin's gc process and gc list commands

This allows operators to do targeted GC work against specific shards, instead of working on all of them.

Signed-off-by: Nathan Hoad <nhoad@bloomberg.net>
AI-Assisted: Used AI to generate the initial implementation.
This commit is contained in:
Nathan Hoad 2026-04-23 10:13:13 -04:00
parent e317e86c8e
commit 12efa485f5
7 changed files with 55 additions and 21 deletions

View File

@ -639,7 +639,7 @@ Options
.. option:: --shard-id=<shard-id>
Optional for mdlog list, bi list, data sync status. Required for ``mdlog trim``.
Optional for mdlog list, bi list, data sync status, gc list, gc process. Required for ``mdlog trim``.
.. option:: --max-entries=<entries>

View File

@ -175,13 +175,18 @@ static int gc_list(const DoutPrefixProvider* dpp, optional_yield y, librados::Io
return cls_rgw_gc_list_decode(bl, entries, truncated, next_marker);
}
int RGWGC::list(int *index, string& marker, uint32_t max, bool expired_only, std::list<cls_rgw_gc_obj_info>& result, bool *truncated, bool& processing_queue)
int RGWGC::list(int *index, string& marker, uint32_t max, bool expired_only, std::list<cls_rgw_gc_obj_info>& result, bool *truncated, bool& processing_queue, std::optional<int> shard_id)
{
result.clear();
string next_marker;
bool check_queue = false;
for (; *index < max_objs && result.size() < max; (*index)++, marker.clear(), check_queue = false) {
int max_index = shard_id.has_value() ? (shard_id.value() + 1) : max_objs;
if (shard_id.has_value()) {
*index = shard_id.value();
}
for (; *index < max_index && result.size() < max; (*index)++, marker.clear(), check_queue = false) {
std::list<cls_rgw_gc_obj_info> entries, queue_entries;
int ret = 0;
@ -236,7 +241,7 @@ int RGWGC::list(int *index, string& marker, uint32_t max, bool expired_only, std
marker = next_marker;
if (*index == max_objs - 1) {
if (*index == max_index - 1) {
if (queue_entries.size() > 0 && *truncated) {
processing_queue = true;
} else {
@ -655,19 +660,26 @@ done:
return 0;
}
int RGWGC::process(bool expired_only, optional_yield y)
int RGWGC::process(bool expired_only, optional_yield y, std::optional<int> shard_id)
{
int max_secs = cct->_conf->rgw_gc_processor_max_time;
const int start = ceph::util::generate_random_number(0, max_objs - 1);
RGWGCIOManager io_manager(this, store->ctx(), this);
for (int i = 0; i < max_objs; i++) {
int index = (i + start) % max_objs;
int ret = process(index, max_secs, expired_only, io_manager, y);
if (shard_id.has_value()) {
// Process only the specified shard
int ret = process(shard_id.value(), max_secs, expired_only, io_manager, y);
if (ret < 0)
return ret;
} else {
// Process all shards with random start
const int start = ceph::util::generate_random_number(0, max_objs - 1);
for (int i = 0; i < max_objs; i++) {
int index = (i + start) % max_objs;
int ret = process(index, max_secs, expired_only, io_manager, y);
if (ret < 0)
return ret;
}
}
if (!going_down()) {
io_manager.drain();

View File

@ -59,11 +59,10 @@ public:
void initialize(CephContext *_cct, RGWRados *_store, optional_yield y);
void finalize();
int list(int *index, std::string& marker, uint32_t max, bool expired_only, std::list<cls_rgw_gc_obj_info>& result, bool *truncated, bool& processing_queue);
void list_init(int *index) { *index = 0; }
int list(int *index, std::string& marker, uint32_t max, bool expired_only, std::list<cls_rgw_gc_obj_info>& result, bool *truncated, bool& processing_queue, std::optional<int> shard_id = std::nullopt);
int process(int index, int process_max_secs, bool expired_only,
RGWGCIOManager& io_manager, optional_yield y);
int process(bool expired_only, optional_yield y);
int process(bool expired_only, optional_yield y, std::optional<int> shard_id = std::nullopt);
bool going_down();
void start_processor();

View File

@ -10635,14 +10635,14 @@ int RGWRados::gc_operate(const DoutPrefixProvider *dpp, string& oid, librados::O
return rgw_rados_operate(dpp, gc_pool_ctx, oid, std::move(op), pbl, y);
}
int RGWRados::list_gc_objs(int *index, string& marker, uint32_t max, bool expired_only, std::list<cls_rgw_gc_obj_info>& result, bool *truncated, bool& processing_queue)
int RGWRados::list_gc_objs(int *index, string& marker, uint32_t max, bool expired_only, std::list<cls_rgw_gc_obj_info>& result, bool *truncated, bool& processing_queue, std::optional<int> shard_id)
{
return gc->list(index, marker, max, expired_only, result, truncated, processing_queue);
return gc->list(index, marker, max, expired_only, result, truncated, processing_queue, shard_id);
}
int RGWRados::process_gc(bool expired_only, optional_yield y)
int RGWRados::process_gc(bool expired_only, optional_yield y, std::optional<int> shard_id)
{
return gc->process(expired_only, y);
return gc->process(expired_only, y, shard_id);
}
int RGWRados::process_lc(const std::unique_ptr<rgw::sal::Bucket>& optional_bucket)

View File

@ -1629,8 +1629,8 @@ public:
librados::ObjectWriteOperation *op);
int gc_operate(const DoutPrefixProvider *dpp, std::string& oid, librados::ObjectReadOperation&& op, bufferlist *pbl, optional_yield y);
int list_gc_objs(int *index, std::string& marker, uint32_t max, bool expired_only, std::list<cls_rgw_gc_obj_info>& result, bool *truncated, bool& processing_queue);
int process_gc(bool expired_only, optional_yield y);
int list_gc_objs(int *index, std::string& marker, uint32_t max, bool expired_only, std::list<cls_rgw_gc_obj_info>& result, bool *truncated, bool& processing_queue, std::optional<int> shard_id = std::nullopt);
int process_gc(bool expired_only, optional_yield y, std::optional<int> shard_id = std::nullopt);
bool process_expired_objects(const DoutPrefixProvider *dpp, optional_yield y);
int process_lc(const std::unique_ptr<rgw::sal::Bucket>& optional_bucket);

View File

@ -420,6 +420,8 @@ void usage()
cout << " mdlog list\n";
cout << " data sync status\n";
cout << " sync error trim\n";
cout << " gc list\n";
cout << " gc process\n";
cout << " required for:\n";
cout << " mdlog trim\n";
cout << " --gen=<gen-id> optional for:\n";
@ -9709,14 +9711,24 @@ next:
}
if (opt_cmd == OPT::GC_LIST) {
if (specified_shard_id) {
int max_gc_shards = min(static_cast<int>(g_ceph_context->_conf->rgw_gc_max_objs), rgw_shards_max());
if (shard_id < 0 || shard_id >= max_gc_shards) {
cerr << "ERROR: shard-id must be in the range [0, " << max_gc_shards - 1 << "]" << std::endl;
return EINVAL;
}
}
int index = 0;
bool truncated;
bool processing_queue = false;
formatter->open_array_section("entries");
std::optional<int> gc_shard_id = specified_shard_id ? std::optional<int>(shard_id) : std::nullopt;
do {
list<cls_rgw_gc_obj_info> result;
int ret = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->list_gc_objs(&index, marker, 1000, !include_all, result, &truncated, processing_queue);
int ret = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->list_gc_objs(&index, marker, 1000, !include_all, result, &truncated, processing_queue, gc_shard_id);
if (ret < 0) {
cerr << "ERROR: failed to list objs: " << cpp_strerror(-ret) << std::endl;
return 1;
@ -9743,6 +9755,14 @@ next:
}
if (opt_cmd == OPT::GC_PROCESS) {
if (specified_shard_id) {
int max_gc_shards = min(static_cast<int>(g_ceph_context->_conf->rgw_gc_max_objs), rgw_shards_max());
if (shard_id < 0 || shard_id >= max_gc_shards) {
cerr << "ERROR: shard-id must be in the range [0, " << max_gc_shards - 1 << "]" << std::endl;
return EINVAL;
}
}
rgw::sal::RadosStore* rados_store = dynamic_cast<rgw::sal::RadosStore*>(driver);
if (!rados_store) {
cerr <<
@ -9752,7 +9772,8 @@ next:
}
RGWRados* store = rados_store->getRados();
int ret = store->process_gc(!include_all, null_yield);
std::optional<int> gc_shard_id = specified_shard_id ? std::optional<int>(shard_id) : std::nullopt;
int ret = store->process_gc(!include_all, null_yield, gc_shard_id);
if (ret < 0) {
cerr << "ERROR: gc processing returned error: " << cpp_strerror(-ret) << std::endl;
return 1;

View File

@ -269,6 +269,8 @@
mdlog list
data sync status
sync error trim
gc list
gc process
required for:
mdlog trim
--gen=<gen-id> optional for: