12 #include "aegis/config.hpp"
13 #include "aegis/rest/rest_controller.hpp"
14 #include "aegis/snowflake.hpp"
20 #include <spdlog/spdlog.h>
25 using rest_call = std::function<rest::rest_reply(rest::request_params)>;
30 using namespace std::chrono;
55 bucket(rest_call & call, asio::io_context & _io_context, std::atomic<int64_t> & global_limit)
60 , _io_context(_io_context)
61 , _global_limit(global_limit)
76 return _global_limit > 0;
92 int64_t time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
100 std::lock_guard<std::mutex> lock(m);
101 while (!can_perform())
106 auto waitfor = milliseconds((reset.load(std::memory_order_relaxed)
107 - std::chrono::duration_cast<milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count()));
108 spdlog::get(
"aegis")->debug(
"Ratelimit almost hit: {}({}) - waiting {}ms", rest::rest_controller::get_method(params.method), params.path, waitfor.count());
109 std::this_thread::sleep_for(waitfor);
112 auto _now = std::chrono::duration_cast<milliseconds>(std::chrono::system_clock::now().time_since_epoch());
113 if (reply.reply_code == 429)
117 spdlog::get(
"aegis")->warn(
"Ratelimit hit - retrying in {}ms...", reset_bypass);
118 std::this_thread::sleep_for(milliseconds(reset_bypass));
122 spdlog::get(
"aegis")->warn(
"Ratelimit hit - retrying in {}s...", reply.retry / 1000);
123 std::this_thread::sleep_for(milliseconds(reply.retry));
125 reply = _call(params);
126 if (reply.reply_code == 429)
127 spdlog::get(
"aegis")->error(
"Ratelimit hit twice. Giving up.");
130 limit.store(reply.limit, std::memory_order_relaxed);
131 remaining.store(reply.remaining, std::memory_order_relaxed);
132 auto http_date = std::chrono::duration_cast<milliseconds>(reply.date.time_since_epoch());
134 reset.store((_now + milliseconds(reset_bypass)).count(), std::memory_order_relaxed);
137 reset.store(reply.reset*1000, std::memory_order_relaxed);
138 _time_delay = (http_date - _now).count();
143 bool ignore_rates =
false;
146 std::queue<std::tuple<std::string, std::string, std::string, std::function<void(rest::rest_reply)>>> _queue;
147 int32_t reset_bypass = 0;
150 asio::io_context & _io_context;
151 std::atomic<int64_t> & _global_limit;
152 std::atomic<int64_t> _time_delay;
REST responses with error_code for possible exception throwing.
Definition: rest_reply.hpp:98
Definition: rest_controller.hpp:43
bool can_perform() const noexcept
Check if bucket can send a message without hitting the ratelimit.
Definition: bucket.hpp:84
std::atomic< int64_t > remaining
Definition: bucket.hpp:67
std::atomic< int64_t > reset
Definition: bucket.hpp:68
std::atomic< int64_t > limit
Definition: bucket.hpp:66
bool is_global() const noexcept
Check if globally ratelimited.
Definition: bucket.hpp:74
bucket(rest_call &call, asio::io_context &_io_context, std::atomic< int64_t > &global_limit)
Definition: bucket.hpp:55
Buckets store ratelimit data per major parameter.
Definition: bucket.hpp:49