Skip to content

Instantly share code, notes, and snippets.

@ghadishayban
Last active May 1, 2019 21:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ghadishayban/98b975e5378518b101924cb98a0e9071 to your computer and use it in GitHub Desktop.
Save ghadishayban/98b975e5378518b101924cb98a0e9071 to your computer and use it in GitHub Desktop.
retry with completablefuture
(ns retry
(:import [java.util.function Supplier]
[java.util.concurrent CompletableFuture TimeUnit]))
(defn with-retry
"given an op wanting retries, and a strategy for backoff,
returns a CompletableFuture that can be waited on
op takes no args
A backoff strategy is a function of an exception, returning nil or a number of milliseconds to backoff"
[op strategy]
(let [cf (CompletableFuture.)]
(CompletableFuture/supplyAsync
(reify Supplier
(get [this]
(try (.complete cf (op))
(catch Exception e
(if-let [b (strategy e)]
(CompletableFuture/supplyAsync this (CompletableFuture/delayedExecutor b TimeUnit/MILLISECONDS))
(.completeExceptionally cf e)))))))
cf))
(defn exponential
"returns a strategy function
the strategy employed is exponential backoff, with a max limit
pred: a predicate of the exception, determining applicability
initial and max: bounds for the backoff in millis
factor: the exponential factor
num-retries: max number of retries"
[pred initial max factor num-retries]
(let [b (atom initial)
n (atom 0)]
(fn [ex]
(when (and (< @n num-retries)
(pred ex))
(swap! n inc)
(first (swap-vals! b (fn [i] (min (* i factor) max))))))))
(comment
(def f (with-retry
(fn API []
(println "trying")
(throw (Exception. "oh no")))
(exponential (constantly true) 400 2000 2 3))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment