I\'m trying to rate-limit the the number of accounts a user can create with my REST API.
I would have liked to use Guava\'s RateLimiter
to only allow an IP
I think I came upon the same problem as in the original question, and based on Louis Wasserman's comment this is what I drew up:
import com.google.common.util.concurrent.RateLimiter;
import java.time.Duration;
public class Titrator {
private final int numDosesPerPeriod;
private final RateLimiter rateLimiter;
private long numDosesAvailable;
private transient final Object doseLock;
public Titrator(int numDosesPerPeriod, Duration period) {
this.numDosesPerPeriod = numDosesPerPeriod;
double numSeconds = period.getSeconds() + period.getNano() / 1000000000d;
rateLimiter = RateLimiter.create(1 / numSeconds);
numDosesAvailable = 0L;
doseLock = new Object();
}
/**
* Consumes a dose from this titrator, blocking until a dose is available.
*/
public void consume() {
synchronized (doseLock) {
if (numDosesAvailable == 0) { // then refill
rateLimiter.acquire();
numDosesAvailable += numDosesPerPeriod;
}
numDosesAvailable--;
}
}
}
The dose meted out by the Titrator is analogous to a permit from a RateLimiter. This implementation assumes that when you consume your first dose, the clock starts ticking on the dosage period. You can consume your max doses per period as fast as you want, but when you reach your max, you have to wait until the period elapses before you can get another dose.
For a tryConsume()
analog to RateLimiter's tryAcquire
, you would check that numDosesAvailable
is positive.