]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: Implement support for --shard-id in radosgw-admin's gc process and gc list commands
authorNathan Hoad <nhoad@bloomberg.net>
Thu, 23 Apr 2026 14:13:13 +0000 (10:13 -0400)
committerNathan Hoad <nhoad@bloomberg.net>
Tue, 23 Jun 2026 14:31:27 +0000 (10:31 -0400)
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.

doc/man/8/radosgw-admin.rst
src/rgw/driver/rados/rgw_gc.cc
src/rgw/driver/rados/rgw_gc.h
src/rgw/driver/rados/rgw_rados.cc
src/rgw/driver/rados/rgw_rados.h
src/rgw/radosgw-admin/radosgw-admin.cc
src/test/cli/radosgw-admin/help.t

index f6e66135f423aac3a7f0fbab7d0979a681fa28bf..f0a054777ad4cd57e4eee2f36cb821780646f1f7 100644 (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>
 
index 24867ce1ac1dd3da03e68bab96c77fc3eda40a41..78d70fe620e952c0f20b6f54324675120ce91b82 100644 (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();
index 3b4c77aeb6b4ee904f1d3413bc2045e216b2ec79..271b2c16cf7aa9d706f06dca079fbee079277b95 100644 (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();
index 4e78e18c2b74bfa0dd9868a61ffd5c6f58a29b0e..7e9b4f6eea8922073561861e294080103e3e596e 100644 (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)
index e7ccb0175aff7a506730f811ea13fe81fddb7d90..96e84f71fbb9829f1128d09ce07eca7a1e395bf7 100644 (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);
index 617b63a2072cb82623eeb4b7effce2b65ef93934..93afcdc0edb7fd1d5dfc55e6b9f816ba39512204 100644 (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;
index b62489296e6878564301da118e9d441f716890b2..a436221d52ecd325d403f3d206f80b996d92b72c 100644 (file)
                                          mdlog list
                                          data sync status
                                          sync error trim
+                                         gc list
+                                         gc process
                                        required for:
                                          mdlog trim
      --gen=<gen-id>                    optional for: