11package com .cunningdj .grokJava ;
22
3- import java .time .LocalDateTime ;
4- import java .util .LinkedList ;
53import java .time .Duration ;
4+ import java .time .LocalDateTime ;
65import java .time .Month ;
6+ import java .util .HashMap ;
7+ import java .util .LinkedList ;
78
9+ /*
10+ * RATE LIMITER - *SLIDING WINDOW ALGORITHM*
11+ */
812class RateLimiter {
9- private LinkedList <LocalDateTime > dtQueue ;
13+ private HashMap < String , LinkedList <LocalDateTime > > dtQueue ;
1014 private final int SECONDS_PER_MINUTE = 60 ;
1115 public final int MAX_REQS_PER_MINUTE ;
1216 // FOR TESTING ONLY (otherwise null)
1317 private final LocalDateTime CURRENT_DT_OVERRIDE ;
1418
1519 public RateLimiter (int maxReqsPerMinute ) {
16- this .dtQueue = new LinkedList <>();
20+ this .dtQueue = new HashMap <>();
1721 this .MAX_REQS_PER_MINUTE = maxReqsPerMinute ;
1822 this .CURRENT_DT_OVERRIDE = null ;
1923 }
2024
21- private RateLimiter (int maxReqsPerMinute , LocalDateTime currentDtOverride ) {
22- // FOR TESTING ONLY
23- this .dtQueue = new LinkedList <>();
24- this .MAX_REQS_PER_MINUTE = maxReqsPerMinute ;
25- this .CURRENT_DT_OVERRIDE = currentDtOverride ;
26- }
27-
2825 // MAIN
2926 public static void main (String [] args ) {
3027 Tester tester = new Tester ();
3128 String testTitle ="" ;
3229
3330 // TEST CLASS METHODS HERE USING TESTER CLASS
3431 testTitle = "RATE_LIMITER" ;
32+ String TEST_USER = "El Duderino" ;
33+ String OTHER_TEST_USER = "Mr. Lebowski" ;
3534 RateLimiter rl = new RateLimiter (3 );
36- tester .isTrue (rl .newRequest (new Request (RateLimiter .makeTestTime (10 , 0 ))), testTitle );
37- tester .isTrue (rl .newRequest (new Request (RateLimiter .makeTestTime (10 , 20 ))), testTitle );
38- tester .isTrue (rl .newRequest (new Request (RateLimiter .makeTestTime (10 , 40 ))), testTitle );
35+ tester .isTrue (rl .newRequest (new Request (TEST_USER , RateLimiter .makeTestTime (10 , 0 ))), testTitle );
36+ tester .isTrue (rl .newRequest (new Request (TEST_USER , RateLimiter .makeTestTime (10 , 20 ))), testTitle );
37+ tester .isTrue (rl .newRequest (new Request (TEST_USER , RateLimiter .makeTestTime (10 , 40 ))), testTitle );
3938 // False, and doesn't add it to the queue
40- tester .isFalse (rl .newRequest (new Request (RateLimiter .makeTestTime (10 , 59 ))), testTitle );
39+ tester .isFalse (rl .newRequest (new Request (TEST_USER , RateLimiter .makeTestTime (10 , 59 ))), testTitle );
40+ // Different user; shouldn't fail
41+ tester .isFalse (rl .newRequest (new Request (OTHER_TEST_USER , RateLimiter .makeTestTime (10 , 59 ))), testTitle );
4142 // True; Skipoed the 10:59 request, and 10:00 request is now > 60s old, leaving 2 within the window during check
42- tester .isTrue (rl .newRequest (new Request (RateLimiter .makeTestTime (11 , 01 ))), testTitle );
43+ tester .isTrue (rl .newRequest (new Request (TEST_USER , RateLimiter .makeTestTime (11 , 01 ))), testTitle );
4344 }
4445
4546 // PUBLIC
4647 public boolean newRequest (Request req ) {
47- if (dtQueue .size () == MAX_REQS_PER_MINUTE ) {
48- while (dtQueue .size () > 0
49- && Duration .between (dtQueue .peek (), req .dt ).getSeconds () > SECONDS_PER_MINUTE ) {
50- dtQueue .poll ();
48+ if (dtQueue .containsKey (req .user )) {
49+ if (dtQueue .get (req .user ).size () == MAX_REQS_PER_MINUTE ) {
50+ while (dtQueue .get (req .user ).size () > 0
51+ && Duration .between (dtQueue .get (req .user ).peek (), req .dt ).getSeconds () > SECONDS_PER_MINUTE ) {
52+ dtQueue .get (req .user ).poll ();
53+ }
5154 }
55+ } else {
56+ LinkedList <LocalDateTime > userRequestQ = new LinkedList <>();
57+ dtQueue .put (req .user , userRequestQ );
5258 }
53- if (dtQueue .size () < MAX_REQS_PER_MINUTE ) {
54- dtQueue .add (req .dt );
59+
60+ if (dtQueue .get (req .user ).size () < MAX_REQS_PER_MINUTE ) {
61+ dtQueue .get (req .user ).add (req .dt );
5562 return true ;
5663 } else {
5764 return false ;
@@ -67,12 +74,15 @@ private static LocalDateTime makeTestTime(int minutes, int seconds) {
6774 // Request
6875 static class Request {
6976 public LocalDateTime dt ;
70- public Request () {
77+ public String user ;
78+ public Request (String user ) {
79+
80+ this .user = user ;
7181 this .dt = LocalDateTime .now ();
7282 }
7383
7484 // TESTING ONLY
75- private Request (LocalDateTime dt ) {
85+ private Request (String user , LocalDateTime dt ) {
7686 this .dt = dt ;
7787 }
7888 }
0 commit comments