Skip to content

Commit 596ed72

Browse files
committed
feat: added a class that implements request frequency control
1 parent aaad656 commit 596ed72

File tree

1 file changed

+134
-0
lines changed

1 file changed

+134
-0
lines changed

include/rateLimiter.h

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#ifndef PERPLEXITY_CPP_RATELIMITER_H
2+
#define PERPLEXITY_CPP_RATELIMITER_H
3+
#pragma once
4+
5+
#include <chrono>
6+
#include <mutex>
7+
#include <deque>
8+
#include <thread>
9+
#include "exceptions.h"
10+
11+
namespace perplexity {
12+
13+
/**
14+
* @brief Rate limiter for controlling the frequency of API requests
15+
*
16+
* Uses the Token Bucket algorithm to limit the rate.
17+
* Thread-safe implementation using mutex.
18+
*/
19+
class RateLimiter {
20+
private:
21+
mutable std::mutex mutex_;
22+
std::deque<std::chrono::steady_clock::time_point> request_times_;
23+
int max_requests_per_minute_;
24+
bool enabled_;
25+
26+
void cleanup_old_requests() {
27+
auto now = std::chrono::steady_clock::now();
28+
auto cutoff = now - std::chrono::minutes(1);
29+
30+
while (!request_times_.empty() && request_times_.front() < cutoff) {
31+
request_times_.pop_front();
32+
}
33+
}
34+
35+
public:
36+
RateLimiter(int max_requests_per_minute = 60, bool enabled = true)
37+
: max_requests_per_minute_(max_requests_per_minute)
38+
, enabled_(enabled) {
39+
if (max_requests_per_minute <= 0) {
40+
throw ConfigurationError("Max requests per minute must be positive");
41+
}
42+
}
43+
44+
/**
45+
* @brief Waiting before the next request (if necessary)
46+
*
47+
* Blocks execution if the limit is reached.
48+
*/
49+
void wait_if_needed() {
50+
if (!enabled_) {
51+
return;
52+
}
53+
54+
std::unique_lock<std::mutex> lock(mutex_);
55+
56+
cleanup_old_requests();
57+
58+
while (request_times_.size() >= static_cast<size_t>(max_requests_per_minute_)) {
59+
auto oldest = request_times_.front();
60+
auto wait_until = oldest + std::chrono::minutes(1);
61+
auto now = std::chrono::steady_clock::now();
62+
63+
if (wait_until > now) {
64+
auto wait_duration = std::chrono::duration_cast<std::chrono::milliseconds>(
65+
wait_until - now
66+
);
67+
68+
lock.unlock();
69+
std::this_thread::sleep_for(wait_duration);
70+
lock.lock();
71+
72+
cleanup_old_requests();
73+
} else {
74+
break;
75+
}
76+
}
77+
78+
// Registering a new request
79+
request_times_.push_back(std::chrono::steady_clock::now());
80+
}
81+
82+
/**
83+
* @brief Checking if it is possible to make a request now
84+
*/
85+
bool can_make_request() const {
86+
if (!enabled_) {
87+
return true;
88+
}
89+
90+
std::lock_guard<std::mutex> lock(mutex_);
91+
const_cast<RateLimiter*>(this)->cleanup_old_requests();
92+
93+
return request_times_.size() <static_cast<size_t>(max_requests_per_minute_);
94+
}
95+
96+
/**
97+
* @brief Getting the current number of requests in the last minute
98+
*/
99+
size_t get_current_request_count() const {
100+
std::lock_guard<std::mutex> lock(mutex_);
101+
const_cast<RateLimiter*>(this)->cleanup_old_requests();
102+
return request_times_.size();
103+
}
104+
105+
/**
106+
* @brief Enabling/disabling rate limiting
107+
*/
108+
void set_enabled(bool enabled) {
109+
std::lock_guard<std::mutex> lock(mutex_);
110+
enabled_ = enabled;
111+
}
112+
113+
/**
114+
* @brief Setting a new limit
115+
*/
116+
void set_limit(int max_requests_per_minute) {
117+
if (max_requests_per_minute <= 0) {
118+
throw ConfigurationError("Max requests per minute must be positive");
119+
}
120+
std::lock_guard<std::mutex> lock(mutex_);
121+
max_requests_per_minute_ = max_requests_per_minute;
122+
}
123+
124+
/**
125+
* @brief Resetting the request counter
126+
*/
127+
void reset() {
128+
std::lock_guard<std::mutex> lock(mutex_);
129+
request_times_.clear();
130+
}
131+
};
132+
133+
} // namespace perplexity
134+
#endif //PERPLEXITY_CPP_RATELIMITER_H

0 commit comments

Comments
 (0)