A general-purpose resilience library for safely calling unreliable external resources (HTTP APIs, databases, message queues, etc.).
Plain data, higher-order function pattern, natural composition via comp / ->.
;; project.clj
[org.clojars.konkon/resilience-clj "0.1.0"]
(require '[resilience-clj.circuit-breaker :as cb])
;; Create
(def breaker (cb/create {:failure-threshold 5
:reset-timeout-ms 10000
:half-open-calls 1
:failure? (fn [result]
(and (map? result)
(>= (:status result) 500)))
:on-state-change (fn [old new]
(println "Circuit:" old "->" new))}))
;; Wrap a function
(def safe-call (cb/wrap breaker http-get))
;; Use
(safe-call "https://api.example.com")
;; => On success: returns http-get result as-is
;; => When open: {:resilience/rejected :circuit-open}
;; Check status
(cb/status breaker) ;; => :closed | :open | :half-open
;; Manual reset
(cb/reset! breaker)
;; Shutdown (stops go-loop)
(cb/shutdown! breaker)
(require '[resilience-clj.retry :as retry])
;; Synchronous
(def resilient-call
(retry/wrap http-get {:max-attempts 3
:backoff-ms 1000
:backoff-factor 2
:jitter 0.1
:retry? (fn [r] (>= (:status r) 500))}))
(resilient-call "https://api.example.com")
;; Asynchronous (returns a core.async channel)
(require '[clojure.core.async :as async])
(def async-call
(retry/wrap-async http-get {:max-attempts 3 :backoff-ms 500}))
(async/<!! (async-call "https://api.example.com"))
(require '[resilience-clj.timeout :as timeout])
(def safe-call (timeout/wrap http-get {:timeout-ms 3000}))
(safe-call "https://api.example.com")
;; => On success: returns http-get result as-is
;; => On timeout: {:resilience/rejected :timeout}
All modules express rejection using the unified format {:resilience/rejected <reason>}.
(require '[resilience-clj.rejected :as rejected])
(rejected/rejected? {:resilience/rejected :timeout}) ;; => true
(rejected/reason {:resilience/rejected :circuit-open}) ;; => :circuit-open
(rejected/reject :custom-reason) ;; => {:resilience/rejected :custom-reason}
;; Circuit Breaker + Retry
(def breaker (cb/create {:failure-threshold 5 :reset-timeout-ms 10000}))
(def resilient-call
(-> http-get
(retry/wrap {:max-attempts 3 :backoff-ms 500})
(->> (cb/wrap breaker))))
(resilient-call "https://api.example.com")
;; retry is applied first; if it still fails, circuit breaker counts the failure
;; Timeout + Retry + Circuit Breaker
(def resilient-call
(-> http-get
(timeout/wrap {:timeout-ms 3000})
(retry/wrap {:max-attempts 3 :backoff-ms 500 :retry? rejected/rejected?})
(->> (cb/wrap breaker))))
comp or -> feels naturalMIT
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |