From: Nathan Hoad Date: Thu, 23 Apr 2026 14:13:13 +0000 (-0400) Subject: rgw: Implement support for --shard-id in radosgw-admin's gc process and gc list commands X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=12efa485f57113086cb7da01fc4ca3a995b51bd6;p=ceph.git 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 AI-Assisted: Used AI to generate the initial implementation. --- diff --git a/doc/man/8/radosgw-admin.rst b/doc/man/8/radosgw-admin.rst index f6e66135f423..f0a054777ad4 100644 --- a/doc/man/8/radosgw-admin.rst +++ b/doc/man/8/radosgw-admin.rst @@ -639,7 +639,7 @@ Options .. option:: --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= diff --git a/src/rgw/driver/rados/rgw_gc.cc b/src/rgw/driver/rados/rgw_gc.cc index 24867ce1ac1d..78d70fe620e9 100644 --- a/src/rgw/driver/rados/rgw_gc.cc +++ b/src/rgw/driver/rados/rgw_gc.cc @@ -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& result, bool *truncated, bool& processing_queue) +int RGWGC::list(int *index, string& marker, uint32_t max, bool expired_only, std::list& result, bool *truncated, bool& processing_queue, std::optional 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 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 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(); diff --git a/src/rgw/driver/rados/rgw_gc.h b/src/rgw/driver/rados/rgw_gc.h index 3b4c77aeb6b4..271b2c16cf7a 100644 --- a/src/rgw/driver/rados/rgw_gc.h +++ b/src/rgw/driver/rados/rgw_gc.h @@ -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& 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& result, bool *truncated, bool& processing_queue, std::optional 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 shard_id = std::nullopt); bool going_down(); void start_processor(); diff --git a/src/rgw/driver/rados/rgw_rados.cc b/src/rgw/driver/rados/rgw_rados.cc index 4e78e18c2b74..7e9b4f6eea89 100644 --- a/src/rgw/driver/rados/rgw_rados.cc +++ b/src/rgw/driver/rados/rgw_rados.cc @@ -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& result, bool *truncated, bool& processing_queue) +int RGWRados::list_gc_objs(int *index, string& marker, uint32_t max, bool expired_only, std::list& result, bool *truncated, bool& processing_queue, std::optional 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 shard_id) { - return gc->process(expired_only, y); + return gc->process(expired_only, y, shard_id); } int RGWRados::process_lc(const std::unique_ptr& optional_bucket) diff --git a/src/rgw/driver/rados/rgw_rados.h b/src/rgw/driver/rados/rgw_rados.h index e7ccb0175aff..96e84f71fbb9 100644 --- a/src/rgw/driver/rados/rgw_rados.h +++ b/src/rgw/driver/rados/rgw_rados.h @@ -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& 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& result, bool *truncated, bool& processing_queue, std::optional shard_id = std::nullopt); + int process_gc(bool expired_only, optional_yield y, std::optional shard_id = std::nullopt); bool process_expired_objects(const DoutPrefixProvider *dpp, optional_yield y); int process_lc(const std::unique_ptr& optional_bucket); diff --git a/src/rgw/radosgw-admin/radosgw-admin.cc b/src/rgw/radosgw-admin/radosgw-admin.cc index 617b63a2072c..93afcdc0edb7 100644 --- a/src/rgw/radosgw-admin/radosgw-admin.cc +++ b/src/rgw/radosgw-admin/radosgw-admin.cc @@ -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= 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(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 gc_shard_id = specified_shard_id ? std::optional(shard_id) : std::nullopt; + do { list result; - int ret = static_cast(driver)->getRados()->list_gc_objs(&index, marker, 1000, !include_all, result, &truncated, processing_queue); + int ret = static_cast(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(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(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 gc_shard_id = specified_shard_id ? std::optional(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; diff --git a/src/test/cli/radosgw-admin/help.t b/src/test/cli/radosgw-admin/help.t index b62489296e68..a436221d52ec 100644 --- a/src/test/cli/radosgw-admin/help.t +++ b/src/test/cli/radosgw-admin/help.t @@ -269,6 +269,8 @@ mdlog list data sync status sync error trim + gc list + gc process required for: mdlog trim --gen= optional for: