diff --git a/include/ucore/rate_limit.h b/include/ucore/rate_limit.h index e7bc651..000dff1 100644 --- a/include/ucore/rate_limit.h +++ b/include/ucore/rate_limit.h @@ -34,12 +34,7 @@ * * 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. + * NOTE. period * tickets * 2 must not exceed the value of a long * * for (;;) { * int fd = accept(..); @@ -59,12 +54,11 @@ struct RateLimit { //internal fields: - //scaled up number of tickets, to avoid floating point - //math - long scaled_tickets; + //how much each ticket is worth. + long ticket_cost; - //number of tickets we have (scaled up) - long available_tickets; + //money we have to "buy" tickets + long funds; //time of the previos period long last_ts; }; @@ -72,6 +66,8 @@ struct RateLimit { /** * Initialize a struct RateLimit, which can hand out @tickets per @period * the perioid must be in the same units as the current_ts + * + * NOTE. period * tickets * 2 must not exceed the range of the RateLimit.funds * * @param r RateLimit to initialize * @param tickets number of tickets (bucket depth) diff --git a/src/rate_limit.c b/src/rate_limit.c index 6bebef0..8b9261a 100644 --- a/src/rate_limit.c +++ b/src/rate_limit.c @@ -1,20 +1,17 @@ #include "ucore/rate_limit.h" -#define SCALE_FACTOR 1000 - - void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long current_ts) { r->tickets = tickets; - r->scaled_tickets = tickets * SCALE_FACTOR; + r->ticket_cost = tickets * period; r->period = period; - r->available_tickets = r->scaled_tickets; + r->funds = r->ticket_cost * tickets; r->last_ts = current_ts; } void uc_ratelimit_reset(struct RateLimit *r, long current_ts) { - r->available_tickets = r->scaled_tickets; + r->funds = r->ticket_cost * r->tickets; if (current_ts) r->last_ts = current_ts; @@ -36,22 +33,21 @@ int uc_ratelimit_allow(struct RateLimit *r, long current_ts) diff_period = 0; } - //Calculate the number of tickets that became available since + //Calculate the cost of tickets that became available since //the last time. - r->available_tickets += diff_period * - (r->scaled_tickets / r->period); + r->funds += diff_period * + ((r->tickets * r->ticket_cost) / r->period); //throttle handing out tickets - // - if (r->available_tickets > r->scaled_tickets) - r->available_tickets = r->scaled_tickets; + if (r->funds > r->ticket_cost * r->tickets) + r->funds = r->ticket_cost * r->tickets; - //If we have at least one ticket, we can allow - if (r->available_tickets >= 1 * SCALE_FACTOR) { - r->available_tickets -= 1 * SCALE_FACTOR; + //If we have enough to buy atleast one ticket, we can allow + if (r->funds >= r->ticket_cost) { + r->funds -= r->ticket_cost; allowed = 1; } else { - //no more tickets. + //not enough to buy a ticket allowed = 0; }