Rate Limiting in OCaml

Here’s the translation of the Go rate limiting example to OCaml, with explanations adapted for OCaml:

open Unix

(* Rate limiting is an important mechanism for controlling resource
   utilization and maintaining quality of service. OCaml can support
   rate limiting using threads and timers. *)

(* First we'll look at basic rate limiting. Suppose
   we want to limit our handling of incoming requests.
   We'll serve these requests off a queue. *)

let requests = Queue.create ()
let () =
  for i = 1 to 5 do
    Queue.add i requests
  done

(* This limiter function will sleep for 200 milliseconds
   between each request. This is the regulator in our
   rate limiting scheme. *)

let limiter () =
  Thread.delay 0.2

(* By calling the limiter function before serving each request,
   we limit ourselves to 1 request every 200 milliseconds. *)

let process_requests () =
  while not (Queue.is_empty requests) do
    limiter ();
    let req = Queue.take requests in
    Printf.printf "request %d %s\n" req (Sys.time () |> string_of_float)
  done

(* For bursty rate limiting, we'll use a semaphore to allow
   short bursts of requests while preserving the overall rate limit. *)

let bursty_semaphore = Semaphore.create 3

(* Fill up the semaphore to represent allowed bursting. *)

let () =
  for _ = 1 to 3 do
    Semaphore.release bursty_semaphore
  done

(* Every 200 milliseconds we'll try to release a new
   value to the semaphore, up to its limit of 3. *)

let replenish_semaphore () =
  while true do
    Thread.delay 0.2;
    Semaphore.release bursty_semaphore
  done

(* Now simulate 5 more incoming requests. The first
   3 of these will benefit from the burst capability
   of our bursty rate limiter. *)

let bursty_requests = Queue.create ()
let () =
  for i = 1 to 5 do
    Queue.add i bursty_requests
  done

let process_bursty_requests () =
  while not (Queue.is_empty bursty_requests) do
    Semaphore.acquire bursty_semaphore;
    let req = Queue.take bursty_requests in
    Printf.printf "request %d %s\n" req (Sys.time () |> string_of_float)
  done

let main () =
  let _ = Thread.create replenish_semaphore () in
  process_requests ();
  process_bursty_requests ()

let () = main ()

Running our program, we would see the first batch of requests handled once every ~200 milliseconds as desired.

For the second batch of requests, we would serve the first 3 immediately because of the burstable rate limiting, then serve the remaining 2 with ~200ms delays each.

This OCaml version uses threads and semaphores to implement rate limiting. The Semaphore module is not part of the OCaml standard library, so you might need to use a third-party implementation or create your own. The basic concept remains the same: we use a combination of delays and a counter (implemented as a semaphore) to control the rate of processing.

Note that OCaml’s threading model is different from Go’s goroutines. OCaml uses system threads, which are more heavyweight than goroutines. For production use, you might want to consider using a more sophisticated concurrency library like Lwt or Async.