Greatly improve the rate limit algorithm, so it is correct for a low
ticket to period ratio.
This commit is contained in:
@@ -34,12 +34,7 @@
|
|||||||
*
|
*
|
||||||
* uc_ratelimit_init(&r,20, 1, time(NULL));
|
* uc_ratelimit_init(&r,20, 1, time(NULL));
|
||||||
*
|
*
|
||||||
* NOTE. The the internals uses integer arithmetic to replenish the tickets,
|
* NOTE. period * tickets * 2 must not exceed the value of a long
|
||||||
* 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 (;;) {
|
* for (;;) {
|
||||||
* int fd = accept(..);
|
* int fd = accept(..);
|
||||||
@@ -59,12 +54,11 @@ struct RateLimit {
|
|||||||
|
|
||||||
//internal fields:
|
//internal fields:
|
||||||
|
|
||||||
//scaled up number of tickets, to avoid floating point
|
//how much each ticket is worth.
|
||||||
//math
|
long ticket_cost;
|
||||||
long scaled_tickets;
|
|
||||||
|
|
||||||
//number of tickets we have (scaled up)
|
//money we have to "buy" tickets
|
||||||
long available_tickets;
|
long funds;
|
||||||
//time of the previos period
|
//time of the previos period
|
||||||
long last_ts;
|
long last_ts;
|
||||||
};
|
};
|
||||||
@@ -72,6 +66,8 @@ struct RateLimit {
|
|||||||
/**
|
/**
|
||||||
* Initialize a struct RateLimit, which can hand out @tickets per @period
|
* Initialize a struct RateLimit, which can hand out @tickets per @period
|
||||||
* the perioid must be in the same units as the current_ts
|
* 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 r RateLimit to initialize
|
||||||
* @param tickets number of tickets (bucket depth)
|
* @param tickets number of tickets (bucket depth)
|
||||||
|
|||||||
+12
-16
@@ -1,20 +1,17 @@
|
|||||||
#include "ucore/rate_limit.h"
|
#include "ucore/rate_limit.h"
|
||||||
|
|
||||||
#define SCALE_FACTOR 1000
|
|
||||||
|
|
||||||
|
|
||||||
void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long current_ts)
|
void uc_ratelimit_init(struct RateLimit *r, long tickets, long period, long current_ts)
|
||||||
{
|
{
|
||||||
r->tickets = tickets;
|
r->tickets = tickets;
|
||||||
r->scaled_tickets = tickets * SCALE_FACTOR;
|
r->ticket_cost = tickets * period;
|
||||||
r->period = period;
|
r->period = period;
|
||||||
r->available_tickets = r->scaled_tickets;
|
r->funds = r->ticket_cost * tickets;
|
||||||
r->last_ts = current_ts;
|
r->last_ts = current_ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
void uc_ratelimit_reset(struct RateLimit *r, long 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)
|
if (current_ts)
|
||||||
r->last_ts = current_ts;
|
r->last_ts = current_ts;
|
||||||
@@ -36,22 +33,21 @@ int uc_ratelimit_allow(struct RateLimit *r, long current_ts)
|
|||||||
diff_period = 0;
|
diff_period = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Calculate the number of tickets that became available since
|
//Calculate the cost of tickets that became available since
|
||||||
//the last time.
|
//the last time.
|
||||||
r->available_tickets += diff_period *
|
r->funds += diff_period *
|
||||||
(r->scaled_tickets / r->period);
|
((r->tickets * r->ticket_cost) / r->period);
|
||||||
|
|
||||||
//throttle handing out tickets
|
//throttle handing out tickets
|
||||||
//
|
if (r->funds > r->ticket_cost * r->tickets)
|
||||||
if (r->available_tickets > r->scaled_tickets)
|
r->funds = r->ticket_cost * r->tickets;
|
||||||
r->available_tickets = r->scaled_tickets;
|
|
||||||
|
|
||||||
//If we have at least one ticket, we can allow
|
//If we have enough to buy atleast one ticket, we can allow
|
||||||
if (r->available_tickets >= 1 * SCALE_FACTOR) {
|
if (r->funds >= r->ticket_cost) {
|
||||||
r->available_tickets -= 1 * SCALE_FACTOR;
|
r->funds -= r->ticket_cost;
|
||||||
allowed = 1;
|
allowed = 1;
|
||||||
} else {
|
} else {
|
||||||
//no more tickets.
|
//not enough to buy a ticket
|
||||||
allowed = 0;
|
allowed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user