Last active
July 25, 2019 17:30
-
-
Save aaronblenkush/53ba36575c8fda9a95fd7ff8a7d9a462 to your computer and use it in GitHub Desktop.
clojure.core.cache pattern
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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))) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; 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