enhance: c.m/backoff add :reset-flow option

We can use worker-flows/online-event-flow
to trigger a retry immediately upon receiving an 'online' event,
instead of waiting for the next retry delay.
This commit is contained in:
rcmerci
2025-04-16 21:56:46 +08:00
parent d73e78598a
commit fd40bbeec5
11 changed files with 100 additions and 38 deletions

View File

@@ -21,32 +21,55 @@
(m/reductions {} init-value)
(m/latest identity))))
(def delays (reductions * 1000 (repeat 2)))
(def ^:private retry-sentinel (js-obj))
(defn backoff
"Retry task when it throw exception `(get ex-data :missionary/retry)`"
[delays-seq task]
(m/sp
(loop [[delay & rest-delays] (seq delays-seq)]
(let [r (try
(m/? task)
(catch :default e
(if (and (some-> e ex-data :missionary/retry)
(pos-int? delay))
(do (m/? (m/sleep delay))
(println :missionary/retry "after" delay "ms (" (ex-message e) ")")
retry-sentinel)
(throw e))))]
(if (identical? r retry-sentinel)
(recur rest-delays)
r)))))
(defn mix
"Return a flow which is mixed by `flows`"
[& flows]
(m/ap (m/?> (m/?> (count flows) (m/seed flows)))))
(def never-flow (m/ap (m/? m/never)))
(def delays (reductions * 1000 (repeat 2)))
(def ^:private retry-sentinel (js-obj))
(defn backoff
"Retry task when it throw exception `(get ex-data :missionary/retry)`
:delay-seq - retry delay-msecs
:reset-flow - retry immediately when getting value from flow and reset delays to init state"
[{:keys [delay-seq reset-flow]
:or {delay-seq (take 4 delays)
reset-flow never-flow}}
task]
(let [reset-flow* (mix reset-flow never-flow)]
(m/sp
(loop [[delay & rest-delays] (seq delay-seq)]
(let [r (try
(m/? task)
(catch :default e
(if (and (some-> e ex-data :missionary/retry)
(pos-int? delay))
(let [delay-or-reset
(m/? (m/race (m/sleep delay :delay)
(m/reduce (fn [_ r] (when r (reduced :reset))) nil
(->> (continue-flow reset-flow*)
(m/eduction (drop 1) (take 1))))))
rest-delays*
(case delay-or-reset
:delay
(do (println :missionary/retry "after" delay "ms (" (ex-message e) ")")
rest-delays)
:reset
(do (println :missionary/retry "retry now (" (ex-message e) ")")
delay-seq))]
[retry-sentinel rest-delays*])
(throw e))))]
(if (and (vector? r)
(first r) ;; if delete this `(first r)`,
;; the code continues to the next line even if r=0...
;; I suspect it's a bug in missionary.
(identical? retry-sentinel (first r)))
(recur (second r))
r))))))
(defn clock
"Return a flow that emits `value` every `interval-ms`."
([interval-ms]