]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rgw: add rate limit for LIST & DELETE ops, hotfix syncup
authorMark Kogan <mkogan@ibm.com>
Wed, 30 Jul 2025 12:54:19 +0000 (12:54 +0000)
committerMark Kogan <mkogan@ibm.com>
Sat, 27 Sep 2025 10:46:27 +0000 (10:46 +0000)
Add rate limiting specific to LIST ops,
similar to the current rate-limiting
(https://docs.ceph.com/en/latest/radosgw/admin/#rate-limit-management)

Example usage:

```
./bin/radosgw-admin ratelimit set --ratelimit-scope=user --uid=<UID> --max_list_ops=2
./bin/radosgw-admin ratelimit set --ratelimit-scope=user --uid=<UID> --max_delete_ops=2
./bin/radosgw-admin ratelimit enable --ratelimit-scope=user --uid=<UID>

./bin/radosgw-admin ratelimit get --ratelimit-scope=user --uid=<UID>
{
  "user_ratelimit": {
    "max_read_ops": 0,
    "max_write_ops": 0,
    "max_list_ops": 2,
    "max_delete_ops": 2,
    "max_read_bytes": 0,
    "max_write_bytes": 0,
    "enabled": true
  }
}

pkill -9 radosgw
./bin/radosgw -c ./ceph.conf ...

aws --endpoint-url 'http://0:8000' s3 mb s3://bkt
aws --endpoint-url 'http://0:8000' s3 cp  ./ceph.conf s3://bkt

aws --endpoint-url http://0:8000 s3api list-objects-v2 --bucket bkt --prefix 'ceph.conf' --delimiter '/'
{
    "Contents": [
        {
            "Key": "ceph.conf",
            "LastModified": "2025-07-30T13:59:38+00:00",
            "ETag": "\"13d11d431ae290134562c019d9e40c0e\"",
            "Size": 32346,
            "StorageClass": "STANDARD"
        }
    ],
    "RequestCharged": null
}

aws --endpoint-url http://0:8000 s3api list-objects-v2 --bucket bkt --prefix 'ceph.conf' --delimiter '/'
{
    "Contents": [
        {
            "Key": "ceph.conf",
            "LastModified": "2025-07-30T13:59:38+00:00",
            "ETag": "\"13d11d431ae290134562c019d9e40c0e\"",
            "Size": 32346,
            "StorageClass": "STANDARD"
        }
    ],
    "RequestCharged": null
}

aws --endpoint-url http://0:8000 s3api list-objects-v2 --bucket bkt --prefix 'ceph.conf' --delimiter '/'
argument of type 'NoneType' is not iterable

tail -F ./out/radosgw.8000.log | grep beast
...
beast: 0x7fffbbe09780:  [30/Jul/2025:15:44:50.359 +0000] " GET /bkt?list-type=2&delimiter=%2F&prefix=ceph.conf&encoding-type=url HTTP/1.1" 200 535 - "aws-cli/2.15.31 Python/3.9.21 Linux/5.14.0-570.28.1.el9_6.x86_64 source/x86_64.rhel.9 prompt/off command/s3api.list-objects-v2" - latency=0.000999995s
beast: 0x7fffbbe09780:  [30/Jul/2025:15:44:53.904 +0000] " GET /bkt?list-type=2&delimiter=%2F&prefix=ceph.conf&encoding-type=url HTTP/1.1" 200 535 - "aws-cli/2.15.31 Python/3.9.21 Linux/5.14.0-570.28.1.el9_6.x86_64 source/x86_64.rhel.9 prompt/off command/s3api.list-objects-v2" - latency=0.000999995s
                                                                                                                                           vvv
beast: 0x7fffbbe09780:  [30/Jul/2025:15:44:58.192 +0000] " GET /bkt?list-type=2&delimiter=%2F&prefix=ceph.conf&encoding-type=url HTTP/1.1" 503 228 - "aws-cli/2.15.31 Python/3.9.21 Linux/5.14.0-570.28.1.el9_6.x86_64 source/x86_64.rhel.9 prompt/off command/s3api.list-objects-v2" - latency=0.000000000s
beast: 0x7fffbbe09780:  [30/Jul/2025:15:44:58.798 +0000] " GET /bkt?list-type=2&delimiter=%2F&prefix=ceph.conf&encoding-type=url HTTP/1.1" 503 228 - "aws-cli/2.15.31 Python/3.9.21 Linux/5.14.0-570.28.1.el9_6.x86_64 source/x86_64.rhel.9 prompt/off command/s3api.list-objects-v2" - latency=0.000999994s
beast: 0x7fffbbe09780:  [30/Jul/2025:15:44:59.807 +0000] " GET /bkt?list-type=2&delimiter=%2F&prefix=ceph.conf&encoding-type=url HTTP/1.1" 503 228 - "aws-cli/2.15.31 Python/3.9.21 Linux/5.14.0-570.28.1.el9_6.x86_64 source/x86_64.rhel.9 prompt/off command/s3api.list-objects-v2" - latency=0.000000000s

s3cmd put ./ceph.conf s3://bkt/1
s3cmd put ./ceph.conf s3://bkt/2
s3cmd put ./ceph.conf s3://bkt/3

s3cmd rm s3://bkt/1
s3cmd rm s3://bkt/2
s3cmd rm s3://bkt/3

delete: 's3://bkt/1'
delete: 's3://bkt/2'
WARNING: Retrying failed request: /3 (503 (SlowDown))
WARNING: Waiting 3 sec...
WARNING: Retrying failed request: /3 (503 (SlowDown))
                                      ^^^
```

Resolves: rhbz#2393774
Resolves: rhbz#2393477
Resolves: rhbz#2395642
Resolves: rhbz#2391529
Resolves: rhbz#2389280

Signed-off-by: Mark Kogan <mkogan@ibm.com>
Update PendingReleaseNotes

Co-authored-by: Yuval Lifshitz <yuvalif@yahoo.com>
Signed-off-by: Mark Kogan <31659604+mkogan1@users.noreply.github.com>
doc/radosgw/admin.rst
src/common/options/rgw.yaml.in
src/rgw/radosgw-admin/radosgw-admin.cc
src/rgw/rgw_process.cc
src/rgw/rgw_ratelimit.h
src/rgw/rgw_rest_ratelimit.cc
src/test/rgw/test_rgw_ratelimit.cc

index a2307344eb772bc706b4fddbe02a3a50ae4186dc..79191b7cad510c8d14854a9600f204ece4a678c0 100644 (file)
@@ -18,7 +18,7 @@ to `Accounts`_ for ease of management.
 
 There are two types of user: 
 
-- **User:** The term "user" refers to user of the S3 interface.
+- **User:** The term "user" refers to  user of the S3 interface.
 
 - **Subuser:** The term "subuser" refers to a user of the Swift interface. A
   subuser is associated with a user. 
@@ -631,6 +631,14 @@ The accumulation interval is configured by the :confval:`rgw_ratelimit_interval`
 The default value is 60 seconds.
 (Note: S3 Multi-Object Delete operation are currently not supported by rate limiting)
 
+The configured limits should be divided by the number of active object gateways. For example,
+if "user A" is to be be limited to 10 ops per minute and there are two object gateways in the cluster,
+then the limit on "user A" should be 5 (10 ops per minute / 2 RGWs).
+If the requests are not balanced between RGWs, the rate limit might be underutilized.
+For example: if the ops limit is 5 and there are two RGWs,
+but the Load Balancer sends load to only one of those RGWs,
+the effective limit is 5 ops, because this limit is enforced per RGW.
+
 Read Requests and Write Requests
 --------------------------------
 Operations that use the ``GET`` method or the ``HEAD`` method in their REST
index 8adefd71384bb5160ba68549ed5fa51e0523a29e..204c075ee09d4e3540a36deab5e15d0d941893cb 100644 (file)
@@ -4438,6 +4438,7 @@ options:
     Requests that exceed the configured rate limits within this time window will be rejected.
     The default is a 60 second token bucket.
   default: 60
+  min: 1
   services:
   - rgw
   flags:
index 029c8a48240f89f353e60db8b55f1338e09d01e3..c1aeb7bf3dfa8a6f9b560987af7bf7ad1e5be509 100644 (file)
@@ -6774,7 +6774,8 @@ int main(int argc, const char **argv)
                                         OPT::ROLE_CREATE, OPT::ROLE_DELETE,
                                         OPT::ROLE_POLICY_PUT, OPT::ROLE_POLICY_DELETE,
                                         OPT::ROLE_POLICY_ATTACH, OPT::ROLE_POLICY_DETACH,
-                                        OPT::USER_POLICY_ATTACH, OPT::USER_POLICY_DETACH};
+                                        OPT::USER_POLICY_ATTACH, OPT::USER_POLICY_DETACH,
+                                        OPT::RATELIMIT_SET, OPT::RATELIMIT_ENABLE, OPT::RATELIMIT_DISABLE};
 
   bool print_warning_message = (non_master_ops_list.find(opt_cmd) != non_master_ops_list.end() &&
                                 non_master_cmd);
index e70b68c0794873876224ef41fcfbc06ea7cc282c..101ed2af6814aaf5d4e494432c0b254da2e9aa8c 100644 (file)
@@ -112,6 +112,14 @@ bool rate_limit(rgw::sal::Driver* driver, req_state* s) {
   s->ratelimit_bucket_marker = bucketfind;
   const char *method = s->info.method;
 
+  bool is_sts_user = (s->auth.identity && s->auth.identity->get_identity_type() == TYPE_ROLE);
+  if (is_sts_user) {
+    ldpp_dout(s, 21) << "STS user detected: uid=" << std::quoted(s->user->get_id().to_str()) << dendl;
+    auto op_ret = s->user->read_attrs(s, s->yield);
+    if (op_ret < 0) {
+      ldpp_dout(s, 0) << "checking rate_limit: uid=" << std::quoted(s->user->get_id().to_str()) << " failed to read user attrs" << dendl;
+    }
+  }
   auto iter = s->user->get_attrs().find(RGW_ATTR_RATELIMIT);
   if(iter != s->user->get_attrs().end()) {
     try {
@@ -126,6 +134,8 @@ bool rate_limit(rgw::sal::Driver* driver, req_state* s) {
       ldpp_dout(s, 0) << "ERROR: failed to decode rate limit" << dendl;
       return -EIO;
     }
+  } else {
+    ldpp_dout(s, 21) << "checking rate_limit: uid=" << std::quoted(s->user->get_id().to_str()) << " does not have a rate limit attribute" << dendl;
   }
   if (s->user->get_id().id == RGW_USER_ANON_ID && global_anon.enabled) {
     *user_ratelimit = global_anon;
index e1b877378ff630bfe9296f565069c4f5b3fff890..4207f1fef8c247dfbcb46379027cf4fad5c9b6f4 100644 (file)
@@ -80,7 +80,7 @@ class RateLimiterEntry {
     del.ops -= fixed_point_rgw_ratelimit;
     return false;
   }
-  bool should_rate_limit_write(int64_t ops_limit, int64_t bw_limit) 
+  bool should_rate_limit_write(int64_t ops_limit, int64_t bw_limit)
   {
     //check if tenants did not reach their bw or ops limits and that the limits are not 0 (which is unlimited)
     if(((write_ops() - 1 < 0) && (ops_limit > 0)) ||
@@ -193,7 +193,7 @@ class RateLimiterEntry {
     }
 };
 
-class RateLimiter {
+class RateLimiter : public DoutPrefix {
 
   static constexpr size_t map_size = 2000000; // will create it with the closest upper prime number
   std::shared_mutex insert_lock;
@@ -256,14 +256,15 @@ class RateLimiter {
     RateLimiter(RateLimiter&&) = delete;
     RateLimiter& operator =(RateLimiter&&) = delete;
     RateLimiter() = delete;
-    RateLimiter(std::atomic_bool& replacing, std::condition_variable& cv)
-      : replacing(replacing), cv(cv)
+    RateLimiter(CephContext* cct, std::atomic_bool& replacing, std::condition_variable& cv)
+      : DoutPrefix(cct, ceph_subsys_rgw, "rate limiter: "), replacing(replacing), cv(cv)
     {
       // prevents rehash, so no iterators invalidation
       ratelimit_entries.max_load_factor(1000);
     };
 
     bool should_rate_limit(const char *method, const std::string& key, ceph::coarse_real_time curr_timestamp, const RGWRateLimitInfo* ratelimit_info, const std::string& resource) {
+      ldpp_dout(this, 21) << "checking should_rate_limit: key=" << std::quoted(key) << " enabled=" << ratelimit_info->enabled << dendl;
       if (key.empty() || key.length() == 1 || !ratelimit_info->enabled)
       {
         return false;
@@ -345,8 +346,8 @@ class ActiveRateLimiter : public DoutPrefix  {
     ActiveRateLimiter(CephContext* cct) :
       DoutPrefix(cct, ceph_subsys_rgw, "rate limiter: ")
     {
-      ratelimit[0] = std::make_shared<RateLimiter>(replacing, cv);
-      ratelimit[1] = std::make_shared<RateLimiter>(replacing, cv);
+      ratelimit[0] = std::make_shared<RateLimiter>(cct, replacing, cv);
+      ratelimit[1] = std::make_shared<RateLimiter>(cct, replacing, cv);
     }
     ~ActiveRateLimiter() {
       ldpp_dout(this, 20) << "stopping ratelimit_gc thread" << dendl;
index 60ae5dd3d205848b227e147890eb1e74d04a138e..26a0179721d454671dfdb59e74c8975a021685e2 100644 (file)
@@ -1,6 +1,11 @@
 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
 // vim: ts=8 sw=2 smarttab ft=cpp
 #include "rgw_rest_ratelimit.h"
+#include "rgw_sal.h"
+#include "rgw_sal_config.h"
+#include "rgw_process_env.h"
+#include "rgw_op.h"
+
 class RGWOp_Ratelimit_Info : public RGWRESTOp {
 int check_caps(const RGWUserCaps& caps) override {
   return caps.check_cap("ratelimit", RGW_CAP_READ);
@@ -247,6 +252,15 @@ void RGWOp_Ratelimit_Set::execute(optional_yield y)
     }
   }
   RESTArgs::get_bool(s, "global", false, &global, nullptr);
+
+  // forward to master zonegroup
+  op_ret = rgw_forward_request_to_master(this, *s->penv.site, s->user->get_id(),
+                                         nullptr, nullptr, s->info, s->err, y);
+  if (op_ret < 0) {
+    ldpp_dout(this, 0) << "ERROR: forward_request_to_master returned ret=" << op_ret << dendl;
+    return;
+  }
+
   set_ratelimit_info(have_max_read_ops, max_read_ops, have_max_write_ops, max_write_ops,
                      have_max_read_bytes, max_read_bytes, have_max_write_bytes, max_write_bytes,
                      have_max_list_ops, max_list_ops, have_max_delete_ops, max_delete_ops,
index c2ffa92b5ce9183ad65c00f1e995d83c88cf3747..fd305fdfad0a8ff93b9ecb4a90e44b0a541d8ef6 100644 (file)
@@ -13,7 +13,7 @@ TEST(RGWRateLimit, op_limit_not_enabled)
   // info.enabled = false, so no limit
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   auto time = ceph::coarse_real_clock::now();
   std::string key = "uuser123";
@@ -25,7 +25,7 @@ TEST(RGWRateLimit, reject_op_over_limit)
   // check that request is being rejected because there are not enough tokens
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_read_ops = 1;
@@ -41,7 +41,7 @@ TEST(RGWRateLimit, accept_op_after_giveback)
   // check that giveback is working fine
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_read_ops = 1;
@@ -58,7 +58,7 @@ TEST(RGWRateLimit, accept_op_after_refill)
   // check that tokens are being filled properly
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_read_ops = 1;
@@ -74,7 +74,7 @@ TEST(RGWRateLimit, reject_bw_over_limit)
   // check that a newer request is rejected if there is no enough tokens (bw)
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_read_bytes = 1;
@@ -91,7 +91,7 @@ TEST(RGWRateLimit, accept_bw)
   // check that when there are enough tokens (bw) the request is still being served
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_read_bytes = 2;
@@ -108,7 +108,7 @@ TEST(RGWRateLimit, check_bw_debt_at_max_120secs)
   // check that the bandwidth debt is not larger than 120 seconds
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_read_bytes = 2;
@@ -125,7 +125,7 @@ TEST(RGWRateLimit, check_that_bw_limit_not_affect_ops)
   // check that high read bytes limit, does not affect ops limit
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_read_ops = 1;
@@ -143,7 +143,7 @@ TEST(RGWRateLimit, read_limit_does_not_affect_writes)
   // read limit does not affect writes
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_read_ops = 1;
@@ -161,7 +161,7 @@ TEST(RGWRateLimit, write_limit_does_not_affect_reads)
   // write limit does not affect reads
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_write_ops = 1;
@@ -180,7 +180,7 @@ TEST(RGWRateLimit, allow_unlimited_access)
   // 0 values in RGWRateLimitInfo should allow unlimited access
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   auto time = ceph::coarse_real_clock::now();
@@ -388,7 +388,7 @@ TEST(RGWRateLimit, reject_list_op_over_limit)
   // check that LIST op is being rejected because there are not enough tokens
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -405,7 +405,7 @@ TEST(RGWRateLimit, accept_list_op_after_giveback)
   // check that giveback is working for LIST ops
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -423,7 +423,7 @@ TEST(RGWRateLimit, accept_list_op_after_refill)
   // check that tokens are being filled properly for LIST ops
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -440,7 +440,7 @@ TEST(RGWRateLimit, list_limit_does_not_affect_reads)
   // list limit does not affect reads
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -458,7 +458,7 @@ TEST(RGWRateLimit, read_limit_does_not_affect_lists)
   // read limit does not affect lists
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -476,7 +476,7 @@ TEST(RGWRateLimit, list_limit_does_not_affect_writes)
   // list limit does not affect writes
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -494,7 +494,7 @@ TEST(RGWRateLimit, write_limit_does_not_affect_lists)
   // write limit does not affect lists
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -512,7 +512,7 @@ TEST(RGWRateLimit, list_limit_does_not_affect_deletes)
   // list limit does not affect deletes
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -530,7 +530,7 @@ TEST(RGWRateLimit, delete_limit_does_not_affect_lists)
   // delete limit does not affect lists
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -549,7 +549,7 @@ TEST(RGWRateLimit, reject_delimiter_op_over_limit)
   // check that DELIMITER op is being rejected because there are not enough tokens
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -566,7 +566,7 @@ TEST(RGWRateLimit, accept_delimiter_op_after_giveback)
   // check that giveback is working for DELIMITER ops
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -584,7 +584,7 @@ TEST(RGWRateLimit, accept_delimiter_op_after_refill)
   // check that tokens are being filled properly for DELIMITER ops
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -602,7 +602,7 @@ TEST(RGWRateLimit, reject_prefix_op_over_limit)
   // check that PREFIX op is being rejected because there are not enough tokens
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -619,7 +619,7 @@ TEST(RGWRateLimit, accept_prefix_op_after_giveback)
   // check that giveback is working for PREFIX ops
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -637,7 +637,7 @@ TEST(RGWRateLimit, accept_prefix_op_after_refill)
   // check that tokens are being filled properly for PREFIX ops
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_list_ops = 1;
@@ -789,7 +789,7 @@ TEST(RGWRateLimit, reject_delete_op_over_limit)
   // check that DELETE op is being rejected because there are not enough tokens
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_delete_ops = 1;
@@ -806,7 +806,7 @@ TEST(RGWRateLimit, accept_delete_op_after_giveback)
   // check that giveback is working for DELETE ops
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_delete_ops = 1;
@@ -824,7 +824,7 @@ TEST(RGWRateLimit, accept_delete_op_after_refill)
   // check that tokens are being filled properly for DELETE ops
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_delete_ops = 1;
@@ -841,7 +841,7 @@ TEST(RGWRateLimit, delete_limit_does_not_affect_reads)
   // delete limit does not affect reads
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_delete_ops = 1;
@@ -859,7 +859,7 @@ TEST(RGWRateLimit, read_limit_does_not_affect_deletes)
   // read limit does not affect deletes
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_delete_ops = 1;
@@ -877,7 +877,7 @@ TEST(RGWRateLimit, write_limit_does_not_affect_deletes)
   // write limit does not affect deletes
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_delete_ops = 1;
@@ -895,7 +895,7 @@ TEST(RGWRateLimit, delete_limit_does_not_affect_writes)
   // delete limit does not affect writes
   std::atomic_bool replacing;
   std::condition_variable cv;
-  RateLimiter ratelimit(replacing, cv);
+  RateLimiter ratelimit(g_ceph_context, replacing, cv);
   RGWRateLimitInfo info;
   info.enabled = true;
   info.max_delete_ops = 1;