Rate Limiting in PureScript
Here’s the translation of the Go rate limiting example to PureScript:
module Main where
import Prelude
import Effect (Effect)
import Effect.Console (log)
import Effect.Now (nowDateTime)
import Effect.Aff (Aff, launchAff_, delay)
import Effect.Aff.AVar (AVar, new, put, take)
import Data.DateTime (DateTime)
import Data.Time.Duration (Milliseconds(..))
import Data.Traversable (for_)
import Control.Parallel (parallel, sequential)
main :: Effect Unit
main = launchAff_ do
-- First we'll look at basic rate limiting. Suppose
-- we want to limit our handling of incoming requests.
-- We'll serve these requests off an AVar of the same name.
requests <- new
for_ (1 .. 5) \i -> put i requests
-- This limiter will receive a value every 200 milliseconds.
-- This is the regulator in our rate limiting scheme.
limiter <- new
-- By blocking on a take from the limiter before serving each request,
-- we limit ourselves to 1 request every 200 milliseconds.
let processRequests = do
req <- take requests
_ <- take limiter
now <- nowDateTime
log $ "request " <> show req <> " " <> show now
-- Start the limiter
let runLimiter = do
delay (Milliseconds 200.0)
put unit limiter
runLimiter
-- Run the limiter and process requests in parallel
sequential $ parallel (processRequests *> processRequests *> processRequests *> processRequests *> processRequests)
*> parallel runLimiter
-- We may want to allow short bursts of requests in
-- our rate limiting scheme while preserving the
-- overall rate limit. We can accomplish this by
-- using multiple slots in our limiter. This burstyLimiter
-- will allow bursts of up to 3 events.
burstyLimiter <- new
-- Fill up the AVar to represent allowed bursting.
for_ (1 .. 3) \_ -> put unit burstyLimiter
-- Every 200 milliseconds we'll try to add a new
-- value to burstyLimiter, up to its limit of 3.
let runBurstyLimiter = do
delay (Milliseconds 200.0)
put unit burstyLimiter
runBurstyLimiter
-- Now simulate 5 more incoming requests. The first
-- 3 of these will benefit from the burst capability
-- of burstyLimiter.
burstyRequests <- new
for_ (1 .. 5) \i -> put i burstyRequests
let processBurstyRequests = do
req <- take burstyRequests
_ <- take burstyLimiter
now <- nowDateTime
log $ "request " <> show req <> " " <> show now
-- Run the bursty limiter and process bursty requests in parallel
sequential $ parallel (processBurstyRequests *> processBurstyRequests *> processBurstyRequests *> processBurstyRequests *> processBurstyRequests)
*> parallel runBurstyLimiter
This PureScript code implements rate limiting using the Effect.Aff
module for asynchronous effects and Effect.Aff.AVar
for synchronization. Here’s a breakdown of the translation:
- We use
AVar
instead of channels for communication between concurrent processes. - The
time.Tick
function is replaced with a recursive function that usesdelay
to simulate ticking. - Goroutines are replaced with parallel computations using the
parallel
andsequential
functions fromControl.Parallel
. - The
fmt.Println
calls are replaced withlog
fromEffect.Console
. - We use
nowDateTime
fromEffect.Now
to get the current time.
The structure and logic of the rate limiting remain the same:
- We first demonstrate basic rate limiting, processing one request every 200 milliseconds.
- Then we show a bursty rate limiter that allows up to 3 requests to be processed immediately, while maintaining the overall rate limit.
To run this program, you would need to compile it with the PureScript compiler and then execute it. The output would be similar to the Go version, showing the timing of each request being processed.
This implementation showcases PureScript’s approach to concurrency and asynchronous programming, utilizing its strong type system and pure functional paradigm while achieving the same rate limiting behavior as the original Go code.