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