diff --git a/src/main/java/com/redislabs/university/RU102J/dao/RateLimiterSlidingDaoRedisImpl.java b/src/main/java/com/redislabs/university/RU102J/dao/RateLimiterSlidingDaoRedisImpl.java index a1b1947..ea3227e 100644 --- a/src/main/java/com/redislabs/university/RU102J/dao/RateLimiterSlidingDaoRedisImpl.java +++ b/src/main/java/com/redislabs/university/RU102J/dao/RateLimiterSlidingDaoRedisImpl.java @@ -1,24 +1,46 @@ package com.redislabs.university.RU102J.dao; +import com.redislabs.university.RU102J.core.KeyHelper; + +import java.time.ZonedDateTime; +import java.util.UUID; + +import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; +import redis.clients.jedis.Response; +import redis.clients.jedis.Transaction; public class RateLimiterSlidingDaoRedisImpl implements RateLimiter { - private final JedisPool jedisPool; - private final long windowSizeMS; - private final long maxHits; - - public RateLimiterSlidingDaoRedisImpl(JedisPool pool, long windowSizeMS, - long maxHits) { - this.jedisPool = pool; - this.windowSizeMS = windowSizeMS; - this.maxHits = maxHits; - } - - // Challenge #7 - @Override - public void hit(String name) throws RateLimitExceededException { - // START CHALLENGE #7 - // END CHALLENGE #7 - } + private final JedisPool jedisPool; + private final long windowSizeMS; + private final long maxHits; + + public RateLimiterSlidingDaoRedisImpl( JedisPool pool, long windowSizeMS, long maxHits ) { + this.jedisPool = pool; + this.windowSizeMS = windowSizeMS; + this.maxHits = maxHits; + } + + // Challenge #7 + @Override + public void hit( String name ) throws RateLimitExceededException { + // START CHALLENGE #7 + try ( Jedis jedis = jedisPool.getResource(); Transaction t = jedis.multi() ) { + long currentMs = ZonedDateTime.now().toInstant().toEpochMilli(); + + // make the member unique in order not to replace the score of another one + String member = String.format( "%s:%s", currentMs, UUID.randomUUID() ); + String key = KeyHelper.getKey( String.format( "limiter:%s:%s:maxHits", windowSizeMS, name ) ); + t.zadd( key, currentMs, member ); + t.zremrangeByScore( key, 0, currentMs - windowSizeMS ); // remove hits before the current sliding window + Response hitCount = t.zcard( key ); + t.exec(); + + if ( hitCount.get() > maxHits ) { + throw new RateLimitExceededException(); + } + } + // END CHALLENGE #7 + } } diff --git a/src/test/java/com/redislabs/university/RU102J/dao/RateLimiterSlidingDaoRedisImplTest.java b/src/test/java/com/redislabs/university/RU102J/dao/RateLimiterSlidingDaoRedisImplTest.java index b54dd0c..aec5131 100644 --- a/src/test/java/com/redislabs/university/RU102J/dao/RateLimiterSlidingDaoRedisImplTest.java +++ b/src/test/java/com/redislabs/university/RU102J/dao/RateLimiterSlidingDaoRedisImplTest.java @@ -47,7 +47,6 @@ public void flush() { keyManager.deleteKeys(jedis); } - @Ignore @Test public void hit() { int exceptionCount = 0; @@ -64,7 +63,6 @@ public void hit() { assertThat(exceptionCount, is(0)); } - @Ignore @Test public void hitOutsideLimit() { int exceptionCount = 0; @@ -81,7 +79,6 @@ public void hitOutsideLimit() { assertThat(exceptionCount, is(2)); } - @Ignore @Test public void hitOutsideWindow() throws InterruptedException { int exceptionCount = 0;