throttling_manager_test.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  * http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 #include <aws/core/utils/Outcome.h>
17 #include <gtest/gtest.h>
18 
19 #include <atomic>
20 #include <thread>
21 #include <cmath>
22 
24 typedef Aws::Utils::Outcome<int, Aws::Client::AWSError<DummyClientErrors>> DummyOutcome;
25 
27 {
28 public:
29  virtual DummyOutcome ThrottledFunction(int number) const
30  {
31  throttled_function_call_count_++;
32  return DummyOutcome(number);
33  }
34  mutable std::atomic<int> throttled_function_call_count_{0};
35 };
36 
38 {
39 public:
40  explicit ThrottledClient(double max_api_tps)
41  {
42  Aws::Client::ThrottlingManager::SetMaxApiTps("ThrottledFunction", max_api_tps);
43  }
44 
46  {
47  throttled_function_call_count_++;
48  auto base_func = [this](int number) -> DummyOutcome {
49  return this->BaseClient::ThrottledFunction(number);
50  };
51  return MakeCall<DummyOutcome, int, DummyClientErrors>(
52  base_func, 0, __func__, DummyClientErrors::THROTTLING_ERROR, true);
53  }
54 
55  mutable std::atomic<int> throttled_function_call_count_{0};
56 };
57 
58 TEST(ThrottlingManagerTest, simpleThrottling)
59 {
60  double max_tps = 100.0;
61  int api_call_count = 100;
62  ThrottledClient throttled_client(max_tps);
63  for (int idx = 0; idx < api_call_count; idx++) {
64  auto outcome = throttled_client.ThrottledFunction();
65  ASSERT_TRUE(outcome.IsSuccess());
66  std::this_thread::sleep_for(std::chrono::milliseconds(int(1000.0 / max_tps)));
67  }
68  /* Double the TPS without changing the limit. Every 2nd call should be throttled and not passed
69  * through to the base client */
70  max_tps *= 2;
71  int succesful_outcomes = 0;
72  for (int idx = 0; idx < api_call_count; idx++) {
73  int call_count_before = throttled_client.BaseClient::throttled_function_call_count_;
74  auto outcome = throttled_client.ThrottledFunction();
75  if (outcome.IsSuccess()) {
76  succesful_outcomes++;
77  } else {
78  ASSERT_EQ(DummyClientErrors::THROTTLING_ERROR, outcome.GetError().GetErrorType());
79  }
80  std::this_thread::sleep_for(std::chrono::milliseconds(int(1000.0 / max_tps)));
81  }
82  ASSERT_NEAR(succesful_outcomes, api_call_count / 2.0, 3);
83 }
84 
89 void MakeCalls(ThrottledClient * throttled_client, std::chrono::milliseconds duration,
90  std::chrono::microseconds sleep_duration)
91 {
92  auto now = std::chrono::steady_clock::now();
93  auto prev_now = now - sleep_duration;
94  auto end = std::chrono::steady_clock::now() + duration;
95  while (now < end) {
96  throttled_client->ThrottledFunction();
97  /* Sleep for sleep_duration but compensate for some of the overhead by subtracting the delta
98  * between now and prev_now. */
99  if (now - prev_now > sleep_duration) {
100  std::this_thread::sleep_for(sleep_duration - (now - prev_now - sleep_duration));
101  } else {
102  std::this_thread::sleep_for(sleep_duration);
103  }
104  prev_now = now;
105  now = std::chrono::steady_clock::now();
106  }
107 }
108 
113 TEST(ThrottlingManagerTest, multiThreadedClientThrottling)
114 {
115  const int milliseconds_to_run = 3500, sleep_duration_in_us = 150, max_tps = 125;
116  int threads_to_spawn = std::max(2u, std::thread::hardware_concurrency());
117  ThrottledClient throttled_client(max_tps);
118  std::vector<std::thread> threads;
119  threads.reserve(threads_to_spawn);
120 for (int tid = 0; tid < threads_to_spawn; tid++) {
121  threads.emplace_back(MakeCalls, &throttled_client,
122  std::chrono::milliseconds(milliseconds_to_run),
123  std::chrono::microseconds(sleep_duration_in_us));
124  }
125  for (auto & t : threads) {
126  t.join();
127  }
128  int expected_non_throttled_call_count = std::ceil(max_tps * milliseconds_to_run / static_cast<float>(1000));
129  ASSERT_LE(throttled_client.BaseClient::throttled_function_call_count_,
130  expected_non_throttled_call_count);
131 }
132 
133 int main(int argc, char ** argv)
134 {
135  testing::InitGoogleTest(&argc, argv);
136  return RUN_ALL_TESTS();
137 }
ThrottledClient(double max_api_tps)
DummyOutcome ThrottledFunction() const
int main(int argc, char **argv)
TEST(ThrottlingManagerTest, simpleThrottling)
virtual DummyOutcome ThrottledFunction(int number) const
void MakeCalls(ThrottledClient *throttled_client, std::chrono::milliseconds duration, std::chrono::microseconds sleep_duration)
Aws::Utils::Outcome< int, Aws::Client::AWSError< DummyClientErrors > > DummyOutcome
void SetMaxApiTps(const std::string &api, double tps)


aws_common
Author(s): AWS RoboMaker
autogenerated on Sat Mar 6 2021 03:11:38