Skip to content

Instantly share code, notes, and snippets.

@vvvvalvalval
Created April 28, 2017 16:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vvvvalvalval/2fc2067cb96270396154dca34a85c083 to your computer and use it in GitHub Desktop.
Save vvvvalvalval/2fc2067cb96270396154dca34a85c083 to your computer and use it in GitHub Desktop.
(defn exponential-backoff
"Implements exponential backoff.
* af is a function which accepts 3 channels (af =success= =error= =retry=), and should do exactly one of the following operations without blocking:
- put a successful value in =success=
- put an error in =error= (will break the loop)
- put an error which causes a retry in =retry=.
* the exponential backoff loop can be configured with :get-delay-ms, a function which returns a (potentially infinite) seq of backoff intervals,
and :imprecision-ms, a maximim number of milliseconds with which to randomly blurr the backoff intervals.
Returns a channel which will receive either the completed result or an error."
[{:as opts
:keys [get-delays-ms imprecision-ms]
:or {get-delays-ms (constantly [1000 2000 4000 8000 16000])
imprecision-ms 1000}}
af]
(let [=success= (a/chan 1)
=retry= (a/chan 1)
=error= (a/chan 1)]
(go-safe
(loop [delays (get-delays-ms)]
(try
(af =success= =retry= =error=)
(catch Throwable err
(throw (ex-info "Error in bs.utils.misc/exponential-backoff body. Means that `af` was badly implemented, as it should never throw."
{} err))))
(a/alt!
=success= ([v] v)
=error= ([err] (throw err))
=retry= ([err] (if-let [delay-ms (first delays)]
(do
(a/<! (a/timeout (+ delay-ms (rand-int imprecision-ms))))
(recur (next delays)))
(throw (ex-info "Failed exponential backoff" {} err)))))
))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment