@@ -2,23 +2,24 @@ package ox.resilience
22
33import scala .concurrent .duration .FiniteDuration
44import ox .*
5-
65import scala .annotation .tailrec
76
8- /** Rate limiter with a customizable algorithm. Operations can be blocked or dropped, when the rate limit is reached. */
7+ /** Rate limiter with a customizable algorithm. Operations can be blocked or dropped, when the rate limit is reached. The rate limiter might
8+ * take into account the start time of the operation, or its entire duration.
9+ */
910class RateLimiter private (algorithm : RateLimiterAlgorithm ):
1011 /** Runs the operation, blocking if the rate limit is reached, until the rate limiter is replenished. */
1112 def runBlocking [T ](operation : => T ): T =
1213 algorithm.acquire()
13- operation
14+ algorithm.runOperation( operation)
1415
15- /** Runs or drops the operation , if the rate limit is reached.
16+ /** Runs the operation or drops it , if the rate limit is reached.
1617 *
1718 * @return
18- * `Some` if the operation has been allowed to run, `None` if the operation has been dropped.
19+ * `Some` if the operation has been run, `None` if the operation has been dropped.
1920 */
2021 def runOrDrop [T ](operation : => T ): Option [T ] =
21- if algorithm.tryAcquire() then Some (operation)
22+ if algorithm.tryAcquire() then Some (algorithm.runOperation( operation) )
2223 else None
2324
2425end RateLimiter
@@ -39,32 +40,36 @@ object RateLimiter:
3940 new RateLimiter (algorithm)
4041 end apply
4142
42- /** Creates a rate limiter using a fixed window algorithm.
43+ /** Creates a rate limiter using a fixed window algorithm. Takes into account the start time of the operation only.
4344 *
4445 * Must be run within an [[Ox ]] concurrency scope, as a background fork is created, to replenish the rate limiter.
4546 *
4647 * @param maxOperations
4748 * Maximum number of operations that are allowed to **start** within a time [[window ]].
4849 * @param window
49- * Interval of time between replenishing the rate limiter. THe rate limiter is replenished to allow up to [[maxOperations ]] in the next
50+ * Interval of time between replenishing the rate limiter. The rate limiter is replenished to allow up to [[maxOperations ]] in the next
5051 * time window.
52+ * @see
53+ * [[fixedWindowWithDuration ]]
5154 */
52- def fixedWindow (maxOperations : Int , window : FiniteDuration )(using Ox ): RateLimiter =
53- apply(RateLimiterAlgorithm .FixedWindow (maxOperations, window))
55+ def fixedWindowWithStartTime (maxOperations : Int , window : FiniteDuration )(using Ox ): RateLimiter =
56+ apply(StartTimeRateLimiterAlgorithm .FixedWindow (maxOperations, window))
5457
55- /** Creates a rate limiter using a sliding window algorithm.
58+ /** Creates a rate limiter using a sliding window algorithm. Takes into account the start time of the operation only.
5659 *
5760 * Must be run within an [[Ox ]] concurrency scope, as a background fork is created, to replenish the rate limiter.
5861 *
5962 * @param maxOperations
6063 * Maximum number of operations that are allowed to **start** within any [[window ]] of time.
6164 * @param window
6265 * Length of the window.
66+ * @see
67+ * [[slidingWindowWithDuration ]]
6368 */
64- def slidingWindow (maxOperations : Int , window : FiniteDuration )(using Ox ): RateLimiter =
65- apply(RateLimiterAlgorithm .SlidingWindow (maxOperations, window))
69+ def slidingWindowWithStartTime (maxOperations : Int , window : FiniteDuration )(using Ox ): RateLimiter =
70+ apply(StartTimeRateLimiterAlgorithm .SlidingWindow (maxOperations, window))
6671
67- /** Rate limiter with token/leaky bucket algorithm.
72+ /** Creates a rate limiter with token/leaky bucket algorithm. Takes into account the start time of the operation only .
6873 *
6974 * Must be run within an [[Ox ]] concurrency scope, as a background fork is created, to replenish the rate limiter.
7075 *
@@ -74,5 +79,40 @@ object RateLimiter:
7479 * Interval of time between adding a single token to the bucket.
7580 */
7681 def leakyBucket (maxTokens : Int , refillInterval : FiniteDuration )(using Ox ): RateLimiter =
77- apply(RateLimiterAlgorithm .LeakyBucket (maxTokens, refillInterval))
82+ apply(StartTimeRateLimiterAlgorithm .LeakyBucket (maxTokens, refillInterval))
83+
84+ /** Creates a rate limiter with a fixed window algorithm.
85+ *
86+ * Takes into account the entire duration of the operation. That is the instant at which the operation "happens" can be anywhere between
87+ * its start and end. This ensures that the rate limit is always respected, although it might make it more restrictive.
88+ *
89+ * Must be run within an [[Ox ]] concurrency scope, as a background fork is created, to replenish the rate limiter.
90+ *
91+ * @param maxOperations
92+ * Maximum number of operations that are allowed to **run** (finishing from previous windows or start new) within a time [[window ]].
93+ * @param window
94+ * Length of the window.
95+ * @see
96+ * [[fixedWindowWithStartTime ]]
97+ */
98+ def fixedWindowWithDuration (maxOperations : Int , window : FiniteDuration )(using Ox ): RateLimiter =
99+ apply(DurationRateLimiterAlgorithm .FixedWindow (maxOperations, window))
100+
101+ /** Creates a rate limiter using a sliding window algorithm.
102+ *
103+ * Takes into account the entire duration of the operation. That is the instant at which the operation "happens" can be anywhere between
104+ * its start and end. This ensures that the rate limit is always respected, although it might make it more restrictive.
105+ *
106+ * Must be run within an [[Ox ]] concurrency scope, as a background fork is created, to replenish the rate limiter.
107+ *
108+ * @param maxOperations
109+ * Maximum number of operations that are allowed to **run** (start or finishing) within any [[window ]] of time.
110+ * @param window
111+ * Length of the window.
112+ * @see
113+ * [[slidingWindowWithStartTime ]]
114+ */
115+ def slidingWindowWithDuration (maxOperations : Int , window : FiniteDuration )(using Ox ): RateLimiter =
116+ apply(DurationRateLimiterAlgorithm .SlidingWindow (maxOperations, window))
117+
78118end RateLimiter
0 commit comments