@Test public void consume100() { RateLimitDescriptor actual = sut.consume(request, 100); assertThat(actual.getMaxBudget(), is(1000L)); assertThat(actual.getFillRate(), is(2L)); assertThat(actual.getCurrentBudget(), is(900L)); }
@Override public RateLimitDescriptor consume(HttpServletRequest request, long consumption) { RateLimitDescriptor descriptor = computeRateLimitRecovery(request); if (descriptor == null) { return null; } String limitationUnitName = descriptor.getLimitationUnitName(); long fillRate = descriptor.getFillRate(); long maxBudget = descriptor.getMaxBudget(); long now = Clock.now().toEpochMillisec(); String tKey = "ratelimit:t:" + limitationUnitName; String cKey = "ratelimit:c:" + limitationUnitName; long delta = consumption; Long ts = redisTemplate.opsForValue().getAndSet(tKey, now); if (ts != null) { long secSinceLastUpdate = now - ts; log.debug("Time (sec) since last update = {}", secSinceLastUpdate); delta -= secSinceLastUpdate * fillRate; } Long carma = redisTemplate.opsForValue().increment(cKey, delta); if (carma == null) { throw new AssertionError("Unexpected condition. can not do in transaction"); } if (carma < consumption) { redisTemplate.opsForValue().set(cKey, consumption); carma = consumption; } log.info("Budget before current request (filled {}): {}", ts == null ? "<unknown>" : (now - ts) * fillRate, ts == null ? maxBudget : maxBudget - carma + consumption); log.info("Budget after current request (consumed {}): {}", consumption, maxBudget - carma); long expire = carma / fillRate; redisTemplate.expire(tKey, expire, TimeUnit.SECONDS); redisTemplate.expire(cKey, expire, TimeUnit.SECONDS); descriptor.setCurrentBudget(maxBudget - carma); return descriptor; }
RedisRateLimitService extends AbstractRateLimitService { @Override public RateLimitDescriptor consume(HttpServletRequest request, long consumption) { RateLimitDescriptor descriptor = computeRateLimitRecovery(request); if (descriptor == null) { return null; } String limitationUnitName = descriptor.getLimitationUnitName(); long fillRate = descriptor.getFillRate(); long maxBudget = descriptor.getMaxBudget(); long now = Clock.now().toEpochMillisec(); String tKey = "ratelimit:t:" + limitationUnitName; String cKey = "ratelimit:c:" + limitationUnitName; long delta = consumption; Long ts = redisTemplate.opsForValue().getAndSet(tKey, now); if (ts != null) { long secSinceLastUpdate = now - ts; log.debug("Time (sec) since last update = {}", secSinceLastUpdate); delta -= secSinceLastUpdate * fillRate; } Long carma = redisTemplate.opsForValue().increment(cKey, delta); if (carma == null) { throw new AssertionError("Unexpected condition. can not do in transaction"); } if (carma < consumption) { redisTemplate.opsForValue().set(cKey, consumption); carma = consumption; } log.info("Budget before current request (filled {}): {}", ts == null ? "<unknown>" : (now - ts) * fillRate, ts == null ? maxBudget : maxBudget - carma + consumption); log.info("Budget after current request (consumed {}): {}", consumption, maxBudget - carma); long expire = carma / fillRate; redisTemplate.expire(tKey, expire, TimeUnit.SECONDS); redisTemplate.expire(cKey, expire, TimeUnit.SECONDS); descriptor.setCurrentBudget(maxBudget - carma); return descriptor; } }
RedisRateLimitService extends AbstractRateLimitService { @Override public RateLimitDescriptor consume(HttpServletRequest request, long consumption) { RateLimitDescriptor descriptor = computeRateLimitRecovery(request); if (descriptor == null) { return null; } String limitationUnitName = descriptor.getLimitationUnitName(); long fillRate = descriptor.getFillRate(); long maxBudget = descriptor.getMaxBudget(); long now = Clock.now().toEpochMillisec(); String tKey = "ratelimit:t:" + limitationUnitName; String cKey = "ratelimit:c:" + limitationUnitName; long delta = consumption; Long ts = redisTemplate.opsForValue().getAndSet(tKey, now); if (ts != null) { long secSinceLastUpdate = now - ts; log.debug("Time (sec) since last update = {}", secSinceLastUpdate); delta -= secSinceLastUpdate * fillRate; } Long carma = redisTemplate.opsForValue().increment(cKey, delta); if (carma == null) { throw new AssertionError("Unexpected condition. can not do in transaction"); } if (carma < consumption) { redisTemplate.opsForValue().set(cKey, consumption); carma = consumption; } log.info("Budget before current request (filled {}): {}", ts == null ? "<unknown>" : (now - ts) * fillRate, ts == null ? maxBudget : maxBudget - carma + consumption); log.info("Budget after current request (consumed {}): {}", consumption, maxBudget - carma); long expire = carma / fillRate; redisTemplate.expire(tKey, expire, TimeUnit.SECONDS); redisTemplate.expire(cKey, expire, TimeUnit.SECONDS); descriptor.setCurrentBudget(maxBudget - carma); return descriptor; }  }
RedisRateLimitService extends AbstractRateLimitService { @Override public RateLimitDescriptor consume(HttpServletRequest request, long consumption) { RateLimitDescriptor descriptor = computeRateLimitRecovery(request); if (descriptor == null) { return null; } String limitationUnitName = descriptor.getLimitationUnitName(); long fillRate = descriptor.getFillRate(); long maxBudget = descriptor.getMaxBudget(); long now = Clock.now().toEpochMillisec(); String tKey = "ratelimit:t:" + limitationUnitName; String cKey = "ratelimit:c:" + limitationUnitName; long delta = consumption; Long ts = redisTemplate.opsForValue().getAndSet(tKey, now); if (ts != null) { long secSinceLastUpdate = now - ts; log.debug("Time (sec) since last update = {}", secSinceLastUpdate); delta -= secSinceLastUpdate * fillRate; } Long carma = redisTemplate.opsForValue().increment(cKey, delta); if (carma == null) { throw new AssertionError("Unexpected condition. can not do in transaction"); } if (carma < consumption) { redisTemplate.opsForValue().set(cKey, consumption); carma = consumption; } log.info("Budget before current request (filled {}): {}", ts == null ? "<unknown>" : (now - ts) * fillRate, ts == null ? maxBudget : maxBudget - carma + consumption); log.info("Budget after current request (consumed {}): {}", consumption, maxBudget - carma); long expire = carma / fillRate; redisTemplate.expire(tKey, expire, TimeUnit.SECONDS); redisTemplate.expire(cKey, expire, TimeUnit.SECONDS); descriptor.setCurrentBudget(maxBudget - carma); return descriptor; }  @Override RateLimitDescriptor consume(HttpServletRequest request, long consumption); @Override RateLimitDescriptor get(HttpServletRequest request); }
RedisRateLimitService extends AbstractRateLimitService { @Override public RateLimitDescriptor consume(HttpServletRequest request, long consumption) { RateLimitDescriptor descriptor = computeRateLimitRecovery(request); if (descriptor == null) { return null; } String limitationUnitName = descriptor.getLimitationUnitName(); long fillRate = descriptor.getFillRate(); long maxBudget = descriptor.getMaxBudget(); long now = Clock.now().toEpochMillisec(); String tKey = "ratelimit:t:" + limitationUnitName; String cKey = "ratelimit:c:" + limitationUnitName; long delta = consumption; Long ts = redisTemplate.opsForValue().getAndSet(tKey, now); if (ts != null) { long secSinceLastUpdate = now - ts; log.debug("Time (sec) since last update = {}", secSinceLastUpdate); delta -= secSinceLastUpdate * fillRate; } Long carma = redisTemplate.opsForValue().increment(cKey, delta); if (carma == null) { throw new AssertionError("Unexpected condition. can not do in transaction"); } if (carma < consumption) { redisTemplate.opsForValue().set(cKey, consumption); carma = consumption; } log.info("Budget before current request (filled {}): {}", ts == null ? "<unknown>" : (now - ts) * fillRate, ts == null ? maxBudget : maxBudget - carma + consumption); log.info("Budget after current request (consumed {}): {}", consumption, maxBudget - carma); long expire = carma / fillRate; redisTemplate.expire(tKey, expire, TimeUnit.SECONDS); redisTemplate.expire(cKey, expire, TimeUnit.SECONDS); descriptor.setCurrentBudget(maxBudget - carma); return descriptor; }  @Override RateLimitDescriptor consume(HttpServletRequest request, long consumption); @Override RateLimitDescriptor get(HttpServletRequest request);  }