117 lines
3.4 KiB
C
117 lines
3.4 KiB
C
#ifndef UC_RATE_LIMIT_H_
|
|
#define UC_RATE_LIMIT_H_
|
|
|
|
/** @file
|
|
* This is a rate limiter, based on the token bucket principle.
|
|
* We have X amount of tickets(=our rate) per Y amount of time,
|
|
*
|
|
* The allowed tickets increases with X per Y amount of time.
|
|
*
|
|
* Bursts will be allowed up till the number of tickets initially
|
|
* specified.
|
|
* e.g. if we are limited to 50 tickets * per 2 second, 50 tickets
|
|
* can be immedietely handed out in the
|
|
* first second. At the 2. second, 25 more tickets become available
|
|
* handed out, at the 3. second 25 more, and so on.
|
|
* (So this is throttleed, so no more than the initial tickets, 50 in
|
|
* this case, can be handed out in any given period)
|
|
*
|
|
* Another example is if we rate 1000 tickets per 60 seconds,
|
|
* 1000 tickets can be handed out the 1. second, 16/17 more the
|
|
* next second, 16/17 more the 3. second and so on.
|
|
*
|
|
* Specify how many tickes we have available per period of time.
|
|
* when work is performed, call uc_ratelimit_cllow() with the current
|
|
* time.
|
|
* The unit of time passed to uc_ratelimit_allow must be the same as
|
|
* for uc_ratelimit_init.
|
|
*
|
|
* Example for rate limiting accepting connections:
|
|
* struct RateLimit r;
|
|
* //Limit to 20 per 1 second, which also limits us to max 20
|
|
* per second. (1 second since we will pass in time that have 1 second resolution)
|
|
* 40 per 2 second would even out to the same rate, but allow a burst of 40
|
|
* (if there's 40 tickets available)
|
|
*
|
|
* uc_ratelimit_init(&r,20, 1, time(NULL));
|
|
*
|
|
* NOTE. (period + 1) * tickets must not exceed the range of the RateLimit.funds
|
|
*
|
|
* for (;;) {
|
|
* int fd = accept(..);
|
|
* if (uc_ratelimit_allow(&r, time(NULL)) {
|
|
* //handle new connection
|
|
* } else {
|
|
* //exceeded limit
|
|
* close(fd);
|
|
* }
|
|
* }
|
|
*/
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
struct RateLimit {
|
|
//how many tickets we want
|
|
long tickets;
|
|
//per this period of time
|
|
long period;
|
|
|
|
//internal fields:
|
|
|
|
//money we have to "buy" tickets
|
|
long funds;
|
|
//time of the previos period
|
|
long last_refill_ts;
|
|
};
|
|
|
|
/** Static initializer for a struct RateLimit
|
|
*
|
|
* @param tickets number of tickets (bucket depth)
|
|
* @param period per this period
|
|
*/
|
|
#define UC_RATELIMIT_INITIALIZER(tickets)\
|
|
{ tickets, period, 0, -period}
|
|
|
|
|
|
|
|
/**
|
|
* Initialize a struct RateLimit, which can hand out @tickets per @period
|
|
* the perioid must be in the same units as the current_ts
|
|
*
|
|
* NOTE. 2 * (period + 1) * tickets must not exceed the range of the RateLimit.funds
|
|
*
|
|
* @param r RateLimit to initialize
|
|
* @param tickets number of tickets (bucket depth)
|
|
* @param period per this period
|
|
*/
|
|
void uc_ratelimit_init(struct RateLimit *r, long tickets, long period);
|
|
|
|
/**
|
|
* Reset a RateLimit, filling up the tickets again.
|
|
*
|
|
* @param r RateLimit to reset
|
|
* @param current ts, if non-zero, re-sets the last_refill_ts to the current_ts
|
|
*/
|
|
void uc_ratelimit_reset(struct RateLimit *r, long current_ts);
|
|
|
|
/**
|
|
* Check if we have 1 ticket available, and can therefore allow
|
|
* the work. Removes 1 ticket if we can, and replenishes the tickets
|
|
* based on the elapsed time since last time the function was called.
|
|
*
|
|
* @param r the RateLimit
|
|
* @param current_ts the current timestamp. Used to calculate the
|
|
* replenisment of tichets since the previous check.
|
|
* @return 1 if we can allow, 0 if we don't.
|
|
*/
|
|
int uc_ratelimit_allow(struct RateLimit *r, long current_ts);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|