Skip to content

Instantly share code, notes, and snippets.

@aaronblenkush
Last active July 25, 2019 17:30
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 aaronblenkush/53ba36575c8fda9a95fd7ff8a7d9a462 to your computer and use it in GitHub Desktop.
Save aaronblenkush/53ba36575c8fda9a95fd7ff8a7d9a462 to your computer and use it in GitHub Desktop.
clojure.core.cache pattern
;; There is a subtle problem with the official clojure.core.cache
;; example (https://github.com/clojure/core.cache/wiki/Using):
(defn get-data [key]
(cache/lookup (swap! cache-store
#(if (cache/has? % key)
(cache/hit % key)
(cache/miss % key (retrieve-data key))))
key))
;; The problem is that the `f` argument to `swap!` "may be called
;; multiple times, and thus should be free of side effects"
;; (https://clojuredocs.org/clojure.core/swap!). The `retrieve-data`
;; function ostensibly has side effects, otherwise we wouldn't be
;; using a cache to store its return value.
;;
;; The solution is to wrap the retrieval in a `delay` to ensure the
;; retrieval only happens once:
(defn get-data [key]
(let [val (delay (retrieve-data key))]
(cache/lookup (swap! cache-store
#(if (cache/has? % key)
(cache/hit % key)
(cache/miss % key @val)))
key)))
;; I find myself using this pattern frequently. It is similar to the pattern above, but does NOT
;; store the status in the cache unless it's a 200 response.
(defn http-status
[url]
(:status (http/get url {:throw-exceptions false})))
;; This is interesting too; it allows us to reload the namespace without initializing the cache
;; back to an empty map `{}`. Usually we'd just use `defonce`, but in this case I want to reset
;; the TTL.
(def http-status-cache
(-> (or (when-let [m (resolve 'http-status-cache)]
(when (bound? m)
@@m))
{})
(cache/ttl-cache-factory :ttl (* 900000 6))
(atom)))
(defn cached-http-status
[url]
(let [val (delay (println "fetching") (http-status url))]
(or
(cache/lookup
(swap!
http-status-cache
#(if (cache/has? % url)
(cache/hit % url)
(let [status (force val)]
(if (= status 200)
(cache/miss % url status)
%))))
url)
(force val))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment