#ifndef UC_RATE_LIMIT_H_ #define UC_RATE_LIMIT_H_ /** 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. The the internals uses integer arithmetic to replenish the tickets, * this can lead to greater errors estimating the available tickets the smaller * the number of tickets is in relation to the period. The arithmetic is scaled * by 1000 currently, so if ticket = 1 and period = 60, we get 1000/60 = 16. This * should be 16.6667, which is an error of about 3.6%, which means we really just * rate limit to 1 per 62 seconds. * * for (;;) { * int fd = accept(..); * if (uc_ratelimit_allow(&r, time(NULL)) { * //handle new connection * } else { * //exceeded limit * close(fd); * } * } */ struct RateLimit { //how many tickets we want long tickets; //per this period of time long period; //internal fields: //scaled up number of tickets, to avoid floating point //math long scaled_tickets; //number of tickets we have (scaled up) long available_tickets; //time of the previos period long last_ts; }; /** * Initialize a struct RateLimit, which can hand out @tickets per @period * the perioid must be in the same units as the current_ts * * @param r RateLimit to initialize * @param tickets number of tickets (bucket depth) * @param period per this period * @param current_ts the current timestamp */ void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long current_ts); /** * Reset a RateLimit, filling up the tickets again. * * @param r RateLimit to reset * @param current ts, if non-zero, re-sets the last_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); #endif