Skip to content

Instantly share code, notes, and snippets.

@gerrit-hntschl
Last active August 29, 2015 14:01
Show Gist options
  • Select an option

  • Save gerrit-hntschl/3d10fa5985b484e57ea1 to your computer and use it in GitHub Desktop.

Select an option

Save gerrit-hntschl/3d10fa5985b484e57ea1 to your computer and use it in GitHub Desktop.
Higher order functions - retries with exponential backoff
(defn retryable
"Returns a function that retries f up to max-attempts, with exponentially
increasing delay, when pred returns truthy for a raised exception.
If max-attempts has been exceeded failure-handler (if any)
is invoked with the exception and arguments of f, in that order.
failure-handler must return an exception."
[f pred & {:keys [failure-handler
max-attempts
backoff-millis]
:or {failure-handler (fn [ex & _] ex)
max-attempts 5
backoff-millis 1000}}]
(letfn [(retrying
[args attempt]
(let [[status result]
(try [::success (apply f args)]
(catch Exception e
(if (pred e)
;; in most cases f should succeed, so only
;; test for max-attempts after an error occurred
(if (< attempt max-attempts)
;; increase waiting time exponentially
(do (Thread/sleep (+ (rand-int 1000)
(* (Math/pow 2 attempt)
backoff-millis)))
;; can't recur from within a catch, so signal a retry
[::retry])
[::failure (apply failure-handler e args)])
;; this is an unexpected exception so re-throw directly
(throw e))))]
(case status
::success result
::retry (recur args (inc attempt))
::failure (throw result))))]
(fn [& args]
(retrying args 1))))
;; usage:
(def upload-file
(retryable upload
(partial instance? java.net.SocketTimeoutException)
:failure-handler (fn [ex & args]
(log/error {:timeout (.getMessage ex)
:args args})
ex)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment