1
+ package com .cunningdj .grokJava ;
2
+
3
+ import java .time .LocalDateTime ;
4
+ import java .util .LinkedList ;
5
+ import java .time .Duration ;
6
+ import java .time .Month ;
7
+
8
+ class RateLimiter {
9
+ private LinkedList <LocalDateTime > dtQueue ;
10
+ private final int SECONDS_PER_MINUTE = 60 ;
11
+ public final int MAX_REQS_PER_MINUTE ;
12
+ // FOR TESTING ONLY (otherwise null)
13
+ private final LocalDateTime CURRENT_DT_OVERRIDE ;
14
+
15
+ public RateLimiter (int maxReqsPerMinute ) {
16
+ this .dtQueue = new LinkedList <>();
17
+ this .MAX_REQS_PER_MINUTE = maxReqsPerMinute ;
18
+ this .CURRENT_DT_OVERRIDE = null ;
19
+ }
20
+
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
+
28
+ // MAIN
29
+ public static void main (String [] args ) {
30
+ Tester tester = new Tester ();
31
+ String testTitle ="" ;
32
+
33
+ // TEST CLASS METHODS HERE USING TESTER CLASS
34
+ testTitle = "RATE_LIMITER" ;
35
+ 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 );
39
+ // False, and doesn't add it to the queue
40
+ tester .isFalse (rl .newRequest (new Request (RateLimiter .makeTestTime (10 , 59 ))), testTitle );
41
+ // 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
+ }
44
+
45
+ // PUBLIC
46
+ 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 ();
51
+ }
52
+ }
53
+ if (dtQueue .size () < MAX_REQS_PER_MINUTE ) {
54
+ dtQueue .add (req .dt );
55
+ return true ;
56
+ } else {
57
+ return false ;
58
+ }
59
+ }
60
+
61
+ // PRIVATE
62
+ // TEST ONLY
63
+ private static LocalDateTime makeTestTime (int minutes , int seconds ) {
64
+ return LocalDateTime .of (2022 , Month .MAY , 10 , 10 , minutes , seconds );
65
+ }
66
+
67
+ // Request
68
+ static class Request {
69
+ public LocalDateTime dt ;
70
+ public Request () {
71
+ this .dt = LocalDateTime .now ();
72
+ }
73
+
74
+ // TESTING ONLY
75
+ private Request (LocalDateTime dt ) {
76
+ this .dt = dt ;
77
+ }
78
+ }
79
+ }
0 commit comments