diff options
Diffstat (limited to 'src/test/rgw/test_rgw_ratelimit.cc')
-rw-r--r-- | src/test/rgw/test_rgw_ratelimit.cc | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/src/test/rgw/test_rgw_ratelimit.cc b/src/test/rgw/test_rgw_ratelimit.cc new file mode 100644 index 000000000..01be4df48 --- /dev/null +++ b/src/test/rgw/test_rgw_ratelimit.cc @@ -0,0 +1,376 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab ft=cpp + +#include <gtest/gtest.h> +#include "rgw_ratelimit.h" + + +using namespace std::chrono_literals; + +TEST(RGWRateLimit, op_limit_not_enabled) +{ + // info.enabled = false, so no limit + std::atomic_bool replacing; + std::condition_variable cv; + RateLimiter ratelimit(replacing, cv); + RGWRateLimitInfo info; + auto time = ceph::coarse_real_clock::now(); + std::string key = "uuser123"; + bool success = ratelimit.should_rate_limit("PUT", key, time, &info); + EXPECT_EQ(false, success); +} +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); + RGWRateLimitInfo info; + info.enabled = true; + info.max_read_ops = 1; + auto time = ceph::coarse_real_clock::now(); + std::string key = "uuser123"; + bool success = ratelimit.should_rate_limit("GET", key, time, &info); + time = ceph::coarse_real_clock::now(); + success = ratelimit.should_rate_limit("GET", key, time, &info); + EXPECT_EQ(true, success); +} +TEST(RGWRateLimit, accept_op_after_giveback) +{ + // check that giveback is working fine + std::atomic_bool replacing; + std::condition_variable cv; + RateLimiter ratelimit(replacing, cv); + RGWRateLimitInfo info; + info.enabled = true; + info.max_read_ops = 1; + auto time = ceph::coarse_real_clock::now(); + std::string key = "uuser123"; + bool success = ratelimit.should_rate_limit("GET", key, time, &info); + ratelimit.giveback_tokens("GET", key); + time = ceph::coarse_real_clock::now(); + success = ratelimit.should_rate_limit("GET", key, time, &info); + EXPECT_EQ(false, success); +} +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); + RGWRateLimitInfo info; + info.enabled = true; + info.max_read_ops = 1; + auto time = ceph::coarse_real_clock::now(); + std::string key = "uuser123"; + bool success = ratelimit.should_rate_limit("GET", key, time, &info); + time += 61s; + success = ratelimit.should_rate_limit("GET", key, time, &info); + EXPECT_EQ(false, success); +} +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); + RGWRateLimitInfo info; + info.enabled = true; + info.max_read_bytes = 1; + auto time = ceph::coarse_real_clock::now(); + std::string key = "uuser123"; + bool success = ratelimit.should_rate_limit("GET", key, time, &info); + ratelimit.decrease_bytes("GET",key, 2, &info); + time = ceph::coarse_real_clock::now(); + success = ratelimit.should_rate_limit("GET", key, time, &info); + EXPECT_EQ(true, success); +} +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); + RGWRateLimitInfo info; + info.enabled = true; + info.max_read_bytes = 2; + auto time = ceph::coarse_real_clock::now(); + std::string key = "uuser123"; + bool success = ratelimit.should_rate_limit("GET", key, time, &info); + ratelimit.decrease_bytes("GET",key, 1, &info); + time = ceph::coarse_real_clock::now(); + success = ratelimit.should_rate_limit("GET", key, time, &info); + EXPECT_EQ(false, success); +} +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); + RGWRateLimitInfo info; + info.enabled = true; + info.max_read_bytes = 2; + auto time = ceph::coarse_real_clock::now(); + std::string key = "uuser123"; + bool success = ratelimit.should_rate_limit("GET", key, time, &info); + ratelimit.decrease_bytes("GET",key, 100, &info); + time += 121s; + success = ratelimit.should_rate_limit("GET", key, time, &info); + EXPECT_EQ(false, success); +} +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); + RGWRateLimitInfo info; + info.enabled = true; + info.max_read_ops = 1; + info.max_read_bytes = 100000000; + auto time = ceph::coarse_real_clock::now(); + std::string key = "uuser123"; + bool success = ratelimit.should_rate_limit("GET", key, time, &info); + ratelimit.decrease_bytes("GET",key, 10000, &info); + time = ceph::coarse_real_clock::now(); + success = ratelimit.should_rate_limit("GET", key, time, &info); + EXPECT_EQ(true, success); +} +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); + RGWRateLimitInfo info; + info.enabled = true; + info.max_read_ops = 1; + info.max_read_bytes = 100000000; + auto time = ceph::coarse_real_clock::now(); + std::string key = "uuser123"; + bool success = ratelimit.should_rate_limit("PUT", key, time, &info); + ratelimit.decrease_bytes("PUT",key, 10000, &info); + time = ceph::coarse_real_clock::now(); + success = ratelimit.should_rate_limit("PUT", key, time, &info); + EXPECT_EQ(false, success); +} +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); + RGWRateLimitInfo info; + info.enabled = true; + info.max_write_ops = 1; + info.max_write_bytes = 100000000; + auto time = ceph::coarse_real_clock::now(); + std::string key = "uuser123"; + bool success = ratelimit.should_rate_limit("GET", key, time, &info); + ratelimit.decrease_bytes("GET",key, 10000, &info); + time = ceph::coarse_real_clock::now(); + success = ratelimit.should_rate_limit("GET", key, time, &info); + EXPECT_EQ(false, success); +} + +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); + RGWRateLimitInfo info; + info.enabled = true; + auto time = ceph::coarse_real_clock::now(); + std::string key = "uuser123"; + bool success = ratelimit.should_rate_limit("GET", key, time, &info); + EXPECT_EQ(false, success); +} + +TEST(RGWRateLimitGC, NO_GC_AHEAD_OF_TIME) +{ + // Test if GC is not starting the replace before getting to map_size * 0.9 + // Please make sure to change those values when you change the map_size in the code + + std::shared_ptr<ActiveRateLimiter> ratelimit(new ActiveRateLimiter(g_ceph_context)); + ratelimit->start(); + auto active = ratelimit->get_active(); + RGWRateLimitInfo info; + auto time = ceph::coarse_real_clock::now(); + std::string key = "uuser123"; + active->should_rate_limit("GET", key, time, &info); + auto activegc = ratelimit->get_active(); + EXPECT_EQ(activegc, active); +} +TEST(RGWRateLimiterGC, GC_IS_WORKING) +{ + // Test if GC is replacing the active RateLimiter + // Please make sure to change those values when you change the map_size in the code + + std::shared_ptr<ActiveRateLimiter> ratelimit(new ActiveRateLimiter(g_ceph_context)); + ratelimit->start(); + auto active = ratelimit->get_active(); + RGWRateLimitInfo info; + info.enabled = true; + auto time = ceph::coarse_real_clock::now(); + std::string key = "-1"; + for(int i = 0; i < 2000000; i++) + { + active->should_rate_limit("GET", key, time, &info); + key = std::to_string(i); + } + auto activegc = ratelimit->get_active(); + EXPECT_NE(activegc, active); +} + + +TEST(RGWRateLimitEntry, op_limit_not_enabled) +{ + // info.enabled = false, so no limit + RateLimiterEntry entry; + RGWRateLimitInfo info; + auto time = ceph::coarse_real_clock::now().time_since_epoch(); + bool success = entry.should_rate_limit(false, &info, time); + EXPECT_EQ(false, success); +} +TEST(RGWRateLimitEntry, reject_op_over_limit) +{ + // check that request is being rejected because there are not enough tokens + + RGWRateLimitInfo info; + RateLimiterEntry entry; + info.enabled = true; + info.max_read_ops = 1; + auto time = ceph::coarse_real_clock::now().time_since_epoch(); + bool success = entry.should_rate_limit(true, &info, time); + time = ceph::coarse_real_clock::now().time_since_epoch(); + success = entry.should_rate_limit(true, &info, time); + EXPECT_EQ(true, success); +} +TEST(RGWRateLimitEntry, accept_op_after_giveback) +{ + // check that giveback is working fine + RGWRateLimitInfo info; + RateLimiterEntry entry; + info.enabled = true; + info.max_read_ops = 1; + auto time = ceph::coarse_real_clock::now().time_since_epoch(); + bool success = entry.should_rate_limit(true, &info, time); + entry.giveback_tokens(true); + time = ceph::coarse_real_clock::now().time_since_epoch(); + success = entry.should_rate_limit(true, &info, time); + EXPECT_EQ(false, success); +} +TEST(RGWRateLimitEntry, accept_op_after_refill) +{ + // check that tokens are being filled properly + RateLimiterEntry entry; + RGWRateLimitInfo info; + info.enabled = true; + info.max_read_ops = 1; + auto time = ceph::coarse_real_clock::now().time_since_epoch(); + bool success = entry.should_rate_limit(true, &info, time); + time += 61s; + success = entry.should_rate_limit(true, &info, time); + EXPECT_EQ(false, success); +} +TEST(RGWRateLimitEntry, reject_bw_over_limit) +{ + // check that a newer request is rejected if there is no enough tokens (bw) + RateLimiterEntry entry; + RGWRateLimitInfo info; + info.enabled = true; + info.max_read_bytes = 1; + auto time = ceph::coarse_real_clock::now().time_since_epoch(); + bool success = entry.should_rate_limit(true, &info, time); + entry.decrease_bytes(true, 2, &info); + time = ceph::coarse_real_clock::now().time_since_epoch(); + success = entry.should_rate_limit(true, &info, time); + EXPECT_EQ(true, success); +} +TEST(RGWRateLimitEntry, accept_bw) +{ + // check that when there are enough tokens (bw) the request is still being served + RateLimiterEntry entry; + RGWRateLimitInfo info; + info.enabled = true; + info.max_read_bytes = 2; + auto time = ceph::coarse_real_clock::now().time_since_epoch(); + bool success = entry.should_rate_limit(true, &info, time); + entry.decrease_bytes(true, 1, &info); + time = ceph::coarse_real_clock::now().time_since_epoch(); + success = entry.should_rate_limit(true, &info, time); + EXPECT_EQ(false, success); +} +TEST(RGWRateLimitEntry, check_bw_debt_at_max_120secs) +{ + // check that the bandwidth debt is not larger than 120 seconds + RateLimiterEntry entry; + RGWRateLimitInfo info; + info.enabled = true; + info.max_read_bytes = 2; + auto time = ceph::coarse_real_clock::now().time_since_epoch(); + bool success = entry.should_rate_limit(true, &info, time); + entry.decrease_bytes(true, 100, &info); + time += 121s; + success = entry.should_rate_limit(true, &info, time); + EXPECT_EQ(false, success); +} +TEST(RGWRateLimitEntry, check_that_bw_limit_not_affect_ops) +{ + // check that high read bytes limit, does not affect ops limit + RateLimiterEntry entry; + RGWRateLimitInfo info; + info.enabled = true; + info.max_read_ops = 1; + info.max_read_bytes = 100000000; + auto time = ceph::coarse_real_clock::now().time_since_epoch(); + bool success = entry.should_rate_limit(true, &info, time); + entry.decrease_bytes(true, 10000, &info); + time = ceph::coarse_real_clock::now().time_since_epoch(); + success = entry.should_rate_limit(true, &info, time); + EXPECT_EQ(true, success); +} +TEST(RGWRateLimitEntry, read_limit_does_not_affect_writes) +{ + // read limit does not affect writes + RateLimiterEntry entry; + RGWRateLimitInfo info; + info.enabled = true; + info.max_read_ops = 1; + info.max_read_bytes = 100000000; + auto time = ceph::coarse_real_clock::now().time_since_epoch(); + bool success = entry.should_rate_limit(false, &info, time); + entry.decrease_bytes(false, 10000, &info); + time = ceph::coarse_real_clock::now().time_since_epoch(); + success = entry.should_rate_limit(false, &info, time); + EXPECT_EQ(false, success); +} +TEST(RGWRateLimitEntry, write_limit_does_not_affect_reads) +{ + // write limit does not affect reads + RateLimiterEntry entry; + RGWRateLimitInfo info; + info.enabled = true; + info.max_write_ops = 1; + info.max_write_bytes = 100000000; + auto time = ceph::coarse_real_clock::now().time_since_epoch(); + std::string key = "uuser123"; + bool success = entry.should_rate_limit(true, &info, time); + entry.decrease_bytes(true, 10000, &info); + time = ceph::coarse_real_clock::now().time_since_epoch(); + success = entry.should_rate_limit(true, &info, time); + EXPECT_EQ(false, success); +} + +TEST(RGWRateLimitEntry, allow_unlimited_access) +{ + // 0 values in RGWRateLimitInfo should allow unlimited access (default value) + RateLimiterEntry entry; + RGWRateLimitInfo info; + info.enabled = true; + auto time = ceph::coarse_real_clock::now().time_since_epoch(); + bool success = entry.should_rate_limit(true, &info, time); + EXPECT_EQ(false, success); +} |