From 26d1bf23b2ba65268b8407915044fa93a4b1edf7 Mon Sep 17 00:00:00 2001 From: quylt Date: Wed, 8 Feb 2023 15:56:20 +0700 Subject: [PATCH] Finish challenge #7 --- .../dao/RateLimiterSlidingDaoRedisImpl.java | 32 ++++++++++++++++++- .../university/RU102J/dao/RedisSchema.java | 6 ++++ .../RateLimiterSlidingDaoRedisImplTest.java | 6 ++-- 3 files changed, 40 insertions(+), 4 deletions(-) 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..8a7c9df 100644 --- a/src/main/java/com/redislabs/university/RU102J/dao/RateLimiterSlidingDaoRedisImpl.java +++ b/src/main/java/com/redislabs/university/RU102J/dao/RateLimiterSlidingDaoRedisImpl.java @@ -1,11 +1,17 @@ package com.redislabs.university.RU102J.dao; +import java.time.Instant; +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 windowSizeMS; // in milli private final long maxHits; public RateLimiterSlidingDaoRedisImpl(JedisPool pool, long windowSizeMS, @@ -20,5 +26,29 @@ public RateLimiterSlidingDaoRedisImpl(JedisPool pool, long windowSizeMS, public void hit(String name) throws RateLimitExceededException { // START CHALLENGE #7 // END CHALLENGE #7 + + String key = RedisSchema.getRateLimiterSlidingKey(windowSizeMS, name, maxHits); + Instant instant = ZonedDateTime.now().toInstant(); + long currTimestampInMilli = instant.toEpochMilli(); + long milestoneTimestampInMilli = currTimestampInMilli - windowSizeMS; + String randomNumber = UUID.randomUUID().toString(); + + try (Jedis jedis = jedisPool.getResource()) { + Transaction multi = jedis.multi(); + + multi.zadd( + key, + currTimestampInMilli, + randomNumber + ); + multi.zremrangeByScore(key, 0, milestoneTimestampInMilli); + Response hits = multi.zcard(key); + + multi.exec(); + + if (hits.get() > maxHits) { + throw new RateLimitExceededException(); + } + } } } diff --git a/src/main/java/com/redislabs/university/RU102J/dao/RedisSchema.java b/src/main/java/com/redislabs/university/RU102J/dao/RedisSchema.java index 630ed6e..02d2eaa 100644 --- a/src/main/java/com/redislabs/university/RU102J/dao/RedisSchema.java +++ b/src/main/java/com/redislabs/university/RU102J/dao/RedisSchema.java @@ -42,6 +42,12 @@ static String getRateLimiterKey(String name, int minuteBlock, String.valueOf(minuteBlock) + ":" + String.valueOf(maxHits)); } + + static String getRateLimiterSlidingKey(long windowSize, String name, long maxHits) { + return KeyHelper.getKey( + String.format("limiter:%s:%s:%s", windowSize, name, maxHits) + ); + } // sites:geo // Redis type: geo 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..f6b7caa 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,7 @@ public void flush() { keyManager.deleteKeys(jedis); } - @Ignore +// @Ignore @Test public void hit() { int exceptionCount = 0; @@ -64,7 +64,7 @@ public void hit() { assertThat(exceptionCount, is(0)); } - @Ignore +// @Ignore @Test public void hitOutsideLimit() { int exceptionCount = 0; @@ -81,7 +81,7 @@ public void hitOutsideLimit() { assertThat(exceptionCount, is(2)); } - @Ignore +// @Ignore @Test public void hitOutsideWindow() throws InterruptedException { int exceptionCount = 0;