Rework rate limit to only refill when needed

This commit is contained in:
Nils O. Selåsdal
2013-10-16 19:22:53 +02:00
parent 2d680052b2
commit 262958b8a0
3 changed files with 49 additions and 49 deletions
+3 -4
View File
@@ -65,8 +65,8 @@ struct RateLimit {
* @param tickets number of tickets (bucket depth) * @param tickets number of tickets (bucket depth)
* @param period per this period * @param period per this period
*/ */
#define UC_RATELIMIT_INITIALIZER(tickets, period)\ #define UC_RATELIMIT_INITIALIZER(tickets)\
{ tickets, period, tickets * period, 0} { tickets, period, 0, -period}
@@ -79,9 +79,8 @@ struct RateLimit {
* @param r RateLimit to initialize * @param r RateLimit to initialize
* @param tickets number of tickets (bucket depth) * @param tickets number of tickets (bucket depth)
* @param period per this period * @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); void uc_ratelimit_init(struct RateLimit *r, long tickets, long period);
/** /**
* Reset a RateLimit, filling up the tickets again. * Reset a RateLimit, filling up the tickets again.
+39 -38
View File
@@ -1,18 +1,18 @@
#include <assert.h> #include <assert.h>
#include <limits.h>
#include "ucore/rate_limit.h" #include "ucore/rate_limit.h"
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)
{ {
r->tickets = tickets; r->tickets = tickets;
r->period = period; r->period = period;
r->funds = r->period * tickets; r->funds = 0; //we fill it on first _allow call
r->last_ts = current_ts; r->last_ts = -period; //-period ensures the first tick can start
//at >= 0 and still refill the bicket
assert(tickets > 0); assert(tickets > 0);
assert(period > 0);
assert(r->period > 0); assert(r->period > 0);
assert(r->funds > 0);
} }
void uc_ratelimit_reset(struct RateLimit *r, long current_ts) void uc_ratelimit_reset(struct RateLimit *r, long current_ts)
@@ -29,43 +29,44 @@ int uc_ratelimit_allow(struct RateLimit *r, long current_ts)
int allowed; int allowed;
long diff_period; long diff_period;
//Find elapsed time if (r->funds >= r->period) {
diff_period = current_ts - r->last_ts; //we have enough
if (diff_period > r->period + 1) {
//help prevent overflow when calculating available_tickets below
diff_period = r->period + 1;
} else if (diff_period < 0) {
//If time went backwards, we halt the time for this round
diff_period = 0;
}
//Calculate the cost of tickets that became available since
//the last time.
r->funds += diff_period * r->tickets;
//the below is the real algorithm, with period =
//tickets * period.
//we have optimized this to the expression used above
//r->funds += diff_period *
// ((r->tickets * r->period) / r->period);
//throttle handing out tickets
if (r->funds > r->period * r->tickets) {
r->funds = r->period * r->tickets;
}
//If we have enough to buy atleast one ticket, we can allow
if (r->funds >= r->period) {
r->funds -= r->period; r->funds -= r->period;
allowed = 1; allowed = 1;
} else { } else { //refill
//not enough to buy a ticket //Find elapsed time
allowed = 0; diff_period = current_ts - r->last_ts;
if (diff_period > r->period + 1) {
//help prevent overflow when calculating available_tickets below
diff_period = r->period + 1;
} else if (diff_period < 0) {
//time went backwards, skip this round
diff_period = 0;
}
//Calculate the cost of tickets that became available
r->funds += diff_period * r->tickets;
//throttle handing out tickets
if (r->funds > r->period * r->tickets) {
r->funds = r->period * r->tickets;
}
//If we have enough to buy atleast one ticket, we can allow
if (r->funds >= r->period) {
r->funds -= r->period;
allowed = 1;
} else {
//not enough to buy a ticket
allowed = 0;
}
//save this period.
r->last_ts = current_ts;
} }
//save this period.
r->last_ts = current_ts;
return allowed; return allowed;
} }
+7 -7
View File
@@ -7,9 +7,9 @@ START_TEST (test_ratelimit_1)
long now = 10; long now = 10;
int i; int i;
uc_ratelimit_init(&r, 30, 2, now); uc_ratelimit_init(&r, 30, 2);
for (i = 0; i < 30; i++) { for (i = 0; i < 30; i++) {
fail_if(!uc_ratelimit_allow(&r, now)); fail_if(!uc_ratelimit_allow(&r, now), "i = %d", i);
} }
fail_if(uc_ratelimit_allow(&r, now)); fail_if(uc_ratelimit_allow(&r, now));
END_TEST END_TEST
@@ -18,7 +18,7 @@ START_TEST (test_ratelimit_2)
struct RateLimit r; struct RateLimit r;
long now = 10; long now = 10;
uc_ratelimit_init(&r, 1, 1, now); uc_ratelimit_init(&r, 1, 1);
fail_if(!uc_ratelimit_allow(&r, now)); fail_if(!uc_ratelimit_allow(&r, now));
fail_if(uc_ratelimit_allow(&r, now)); fail_if(uc_ratelimit_allow(&r, now));
@@ -34,7 +34,7 @@ START_TEST (test_ratelimit_3)
long now = 10; long now = 10;
int i; int i;
uc_ratelimit_init(&r, 10, 5, now); uc_ratelimit_init(&r, 10, 5);
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
fail_if(!uc_ratelimit_allow(&r, now)); fail_if(!uc_ratelimit_allow(&r, now));
@@ -70,7 +70,7 @@ START_TEST (test_ratelimit_4)
long now = 10; long now = 10;
int i; int i;
uc_ratelimit_init(&r, 10, 5, now); uc_ratelimit_init(&r, 10, 5);
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
fail_if(!uc_ratelimit_allow(&r, now)); fail_if(!uc_ratelimit_allow(&r, now));
@@ -92,7 +92,7 @@ START_TEST (test_ratelimit_5)
struct RateLimit r; struct RateLimit r;
long now = 1400000000; long now = 1400000000;
uc_ratelimit_init(&r, 1, 10, now); uc_ratelimit_init(&r, 1, 10);
fail_if(!uc_ratelimit_allow(&r, now)); fail_if(!uc_ratelimit_allow(&r, now));
fail_if(uc_ratelimit_allow(&r, now)); fail_if(uc_ratelimit_allow(&r, now));
@@ -117,7 +117,7 @@ START_TEST (test_ratelimit_time_backward)
struct RateLimit r; struct RateLimit r;
long now = 1400000000; long now = 1400000000;
uc_ratelimit_init(&r, 1, 10, now); uc_ratelimit_init(&r, 1, 10);
fail_if(!uc_ratelimit_allow(&r, now)); fail_if(!uc_ratelimit_allow(&r, now));
fail_if(uc_ratelimit_allow(&r, now)); fail_if(uc_ratelimit_allow(&r, now));