Skip to content

Clojure wrapper for Bucket4J rate-limiting library based on token-bucket algorithm.

License

Notifications You must be signed in to change notification settings

fr33m0nk/clj-bucket4j

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fr33m0nk/clj-bucket4j

clj-bucket4j is a a simple library that wraps over Bucket4J and offers convenience methods for easy implementation in Clojure code. For further documentation, do refer Bucket4J official docs.

Clojars Project

Usage

All functions are available through the fr33m0nk.clj-bucket4j namespace.

Add the following to your project dependencies:

  • CLI/deps.edn dependency information
net.clojars.fr33m0nk/clj-bucket4j {:mvn/version "0.1.4"}
  • Leningen/Boot
[net.clojars.fr33m0nk/clj-bucket4j "0.1.4"]
  • Maven
<dependency>
  <groupId>net.clojars.fr33m0nk</groupId>
  <artifactId>clj-bucket4j</artifactId>
  <version>0.1.4</version>
</dependency>
  • Gradle
implementation("net.clojars.fr33m0nk:clj-bucket4j:0.1.4")

Example usages

Require at the REPL with:

(require '[fr33m0nk.clj-bucket4j :as b4j])

Imagine that you have a thread-pool executor and you want to know what your threads are doing at the moment when thread-pool throws RejectedExecutionException. Printing stack traces of all threads in the JVM will be the best way to know where are all threads have stuck and why the thread pool is overflown. But acquiring stack traces is a very cost operation by itself, and you want to do it not often than 1 time per 10 minutes:

(import java.util.concurrent.Executors)
(import java.util.concurrent.RejectedExecutionException)
(import java.lang.management.ManagementFactory)
(import java.util.concurrent.TimeUnit)

;; define the limit 1 time per 10 minutes
(def simple-bandwidth (b4j/simple-bandwidth 1 6000000))

;; construct the bucket
(def bucket (-> (b4j/bucket-builder)
                (b4j/add-limit simple-bandwidth)
                (b4j/build)))

(def executor (Executors/newSingleThreadExecutor))

(try
  (->> (range 10)
       (mapv #(do
                ;; simulate Executor failure
                (when (> % 5)
                  (.shutdown executor)
                  (.awaitTermination executor 1 TimeUnit/MINUTES))
                (.execute executor ^Runnable (fn []
                                               (println "processing this " %)
                                               (Thread/sleep 1000)
                                               (println "I am done"))))))
  (catch RejectedExecutionException ex
    (when (b4j/try-consume bucket 1)
      (let [thread-info-list (.dumpAllThreads (ManagementFactory/getThreadMXBean) true true)]
        ;; Dummy log fn
        (log-somewhere thread-info-list)))
    (throw ex)))

Suppose you need to have a fresh exchange rate between dollars and euros. To get the rate you continuously poll the third-party provider, and by contract with the provider you should poll not often than 100 times per 1 minute, else provider will block your IP:

;; define the limit 100 times per 1 minute
(def simple-bandwidth (b4j/simple-bandwidth 100 60000))

;; construct the bucket
(def bucket (-> (b4j/bucket-builder)
                (b4j/add-limit simple-bandwidth)
                (b4j/build)))
                 
(def exchange-rates (atom 0.0))                 

;; do polling in infinite loop
(while true
  ;; Consume a token from the token bucket.
  ;; If a token is not available this function will park the thread until the refill adds one to the bucket.
  (b4j/block-and-consume bucket 1)
  
  (swap! exchange-rate #(identity %2) (poll-exchange-rate)))
  

License

Copyright © 2023 Prashant Sinha

Distributed under the MIT License.

About

Clojure wrapper for Bucket4J rate-limiting library based on token-bucket algorithm.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published