                      :eoq-backoff-ms eoq-backoff-ms
                      :throttle-ms    throttle-ms})]
     (when auto-start? (start w)) w))
-
-(comment
-  (def w1 (worker {} "myq"))
-  (wcar {} (enqueue "myq" "msg"))
-  (stop w1)
-  (queue-status {} "myq"))
 
 ;;;; Renamed/deprecated
 

;;;; Public utils

(defn exp-backoff "Returns binary exponential backoff value."
  [attempt & [{:keys [factor min max]
               :or   {factor 2000}}]]
  (let [binary-exp (Math/pow 2 (dec attempt))
        time (* (+ binary-exp (rand binary-exp)) 0.5 factor)]
    (long (let [time (if min (clojure.core/max min time) time)
                time (if max (clojure.core/min max time) time)]
            time))))

(comment (map #(exp-backoff % {}) (range 10)))

;;;; Admin


    \"eoq-backoff\" - If circle uninitialized or end-of-circle marker reached.
    [<mid> <mcontent> <attempt-count>] - If message should be (re)handled now."
  [qname & [{:keys [lock-ms eoq-backoff-ms]
             :or   {lock-ms (* 1000 60 60) eoq-backoff-ms exp-backoff}}]]
  (let [;; Precomp 5 backoffs so that `dequeue` can init the backoff
        ;; atomically. This is hackish, but a decent tradeoff.
        eoq-backoff-ms-vec
        (cond (fn?      eoq-backoff-ms) (mapv eoq-backoff-ms              (range 5))
              (integer? eoq-backoff-ms) (mapv (constantly eoq-backoff-ms) (range 5))
              :else (throw (Exception. (str "Bad eoq-backoff-ms: " eoq-backoff-ms))))]
    (car/lua
     "if redis.call('exists', _:qk-eoq-backoff) == 1 then return 'eoq-backoff' end

     -- TODO Waiting for Lua brpoplpush support to get us long polling
     local mid = redis.call('rpoplpush', _:qk-mid-circle, _:qk-mid-circle)
     local now = tonumber(_:now)

     if (not mid) or (mid == 'end-of-circle') then -- Uninit'd or eoq

       -- Calculate eoq_backoff_ms
       local ndry_runs = tonumber(redis.call('get', _:qk-ndry-runs) or 0)
       local eoq_ms_tab = {_:eoq-ms0, _:eoq-ms1, _:eoq-ms2, _:eoq-ms3, _:eoq-ms4}
       local eoq_backoff_ms = tonumber(eoq_ms_tab[math.min(5, (ndry_runs + 1))])

       -- Set queue-wide polling backoff flag
       redis.call('psetex', _:qk-eoq-backoff, eoq_backoff_ms, 'true')
       redis.call('incr',   _:qk-ndry-runs)

       -- Prune expired backoffs. We have to do this manually if we want to keep
       -- backoffs in a hash as we do for efficiency & Cluster support. This hash
       -- is usu. quite small since it consists only of active retry and dedupe
       -- backoffs.
       for i,k in pairs(redis.call('hkeys', _:qk-backoffs)) do
         if (now >= tonumber(redis.call('hget', _:qk-backoffs, k))) then
           redis.call('hdel', _:qk-backoffs, k)
         end
       end

       return 'eoq-backoff'
     end

     if redis.call('sismember', _:qk-gc, mid) == 1 then -- GC
       redis.call('lrem', _:qk-mid-circle, 1, mid) -- Efficient here
       redis.call('srem', _:qk-gc,            mid)
       redis.call('hdel', _:qk-msgs,          mid)
       redis.call('hdel', _:qk-locks,         mid)
       --redis.call('hdel', _:qk-backoffs,    mid) -- No! Will use as dedupe backoff
       redis.call('hdel', _:qk-nattempts,     mid)
       redis.call('set',  _:qk-ndry-runs, 0) -- Did work
       return nil
     end

     local lock_exp    = tonumber(redis.call('hget', _:qk-locks,    mid) or 0)
     local backoff_exp = tonumber(redis.call('hget', _:qk-backoffs, mid) or 0)

     -- Active lock/backoff
     if (now < lock_exp) or (now < backoff_exp) then return nil end

     redis.call('hset', _:qk-locks,    mid, now + tonumber(_:lock-ms)) -- Acquire
     redis.call('hdel', _:qk-backoffs, mid) -- Expired, so prune (keep gc-prune fast)
     redis.call('set',  _:qk-ndry-runs, 0) -- Did work

     local mcontent  = redis.call('hget',    _:qk-msgs,      mid)
     local nattempts = redis.call('hincrby', _:qk-nattempts, mid, 1)
     return {mid, mcontent, nattempts}"
     {:qk-msgs        (qkey qname :msgs)
      :qk-locks       (qkey qname :locks)
      :qk-backoffs    (qkey qname :backoffs)
      :qk-nattempts   (qkey qname :nattempts)
      :qk-mid-circle  (qkey qname :mid-circle)
      :qk-gc          (qkey qname :gc)
      :qk-eoq-backoff (qkey qname :eoq-backoff?)
      :qk-ndry-runs   (qkey qname :ndry-runs)}
     {:now            (System/currentTimeMillis)
      :lock-ms        lock-ms
      :eoq-ms0        (nth eoq-backoff-ms-vec 0)
      :eoq-ms1        (nth eoq-backoff-ms-vec 1)
      :eoq-ms2        (nth eoq-backoff-ms-vec 2)
      :eoq-ms3        (nth eoq-backoff-ms-vec 3)
      :eoq-ms4        (nth eoq-backoff-ms-vec 4)})))

(comment
  (clear-queues {} :q1)
  (queue-status {} :q1)
  (wcar {} (enqueue :q1 :msg1 :mid1))
  (wcar {} (message-status :q1 :mid1))
  (wcar {} (dequeue :q1))
  ;;(mapv exp-backoff (range 5))
  (wcar {} (car/pttl (qkey :q1 :eoq-backoff?))))

(defn handle1 "Implementation detail!"
  [conn qname handler [mid mcontent attempt :as poll-reply]]
