Welcome! Please see the About page for a little more info on how this works.

0 votes
ago in core.cache by
closed ago by

As of version 1.2.254 of org.clojure/core.cache, using a value-fn that throws will cause an entry to be added to the cache associated with a Delay with :status :failed and the exception as a value.

The code below can be used to compare the behavior between versions 1.1.234 and 1.2.254.

(require '[clojure.core.cache.wrapped :as cw])

(def counter (atom 0))

(defn value-fn [v]
  (prn :value-fn-called)
  (swap! counter inc)
  (if (= @counter 1)
    (throw (ex-info "thrown" {}))
    v))

(def test-cache (cw/lu-cache-factory {} :threshold 10))

(reset! counter 0)
; this will throw
(cw/lookup-or-miss test-cache "throw" value-fn)
; in 1.1.234 this will call value-fn again and not throw; in 1.2.254, it won't call and return
; the cached delay result, which is an exception
(cw/lookup-or-miss test-cache "throw" value-fn)

; evict to force call to value-fn
(cw/evict test-cache "throw")

This is related to work done in CCACHE-65 to avoid cache stampede in multi-threaded applications, and could be related to what has been reported in CMEMOIZE-31.

This was initially reported in Clojurians Slack.

closed with the note: Fixed in 1.2.263

2 Answers

0 votes
ago by
edited ago by

A 1.2.259 version should be available shortly. It no longer caches exceptions, but it does so in a way that currently requires locking on the atom to avoid a race condition between the attempted cache update and an eviction of the failed value-fn. This is a temporary fix until I figure out a better approach that can both avoid a cache stampede and avoid caching exceptions.

Edit: 1.2.263 is available now, without locking.

ago by
My concern with the current `(locking)` approach is that it wraps over the `value-fn/wrap-fn` call, which could lead to contention.

On the other hand, the current behavior of `(lookup-or-miss)` is actually what the function promises - the `value-fn/wrap-fn` will only be called once.

So maybe a middle ground, at least for now, would be to leave it as is and provide a `(safe-lookup-or-miss)` that checks if the result of `value-fn` is an exception and uses both RetryingDelay as well as locking?
ago by
As noted, this is a temporary fix while I think about the problem.

It's hard to prevent both cache stampede and caching exceptions. I can easily solve one without solving the other. To prevent the cache stampede, you can't just "check the result of value-fn" because that means eagerly evaluating it -- a cache stampede.
0 votes
ago by

Version 1.2.263 solves both the cache stampede bug and the cached exceptions bug, and does so without locking. Thank you, Claude Opus 4.6.

...