CLJS-specific implementation code for [[metabase.util.memoize]]. Not to be referenced from other code! | (ns metabase.util.memoize.impl.js (:require [cljs.cache :as cache])) |
Similar to clojure.lang.Delay, but will not memoize an exception and will instead retry. fun - the function, never nil available? - indicates a memoized value is available, volatile for visibility value - the value (if available) - volatile for visibility | (deftype RetryingDelay [fun ^:volatile-mutable available? ^:volatile-mutable value] IDeref (-deref [_] ;; first check (safe with volatile flag) (if available? value ;; fun may throw - will retry on next deref (let [v (fun)] (set! value v) (set! available? true) v))) IPending (-realized? [_] available?)) |
(defn- d-lay [fun] (->RetryingDelay fun false nil)) | |
If a value is not already derefable, wrap it up. This is used to help rebuild seed/base maps passed in to the various caches so that they conform to core.memoize's world view. | (defn- make-derefable [v] (if (instance? IDeref v) v (reify IDeref (-deref [_] v)))) |
Given a seed/base map, ensure all the values in it are derefable. | (defn- derefable-seed [seed] (update-vals seed make-derefable)) |
(deftype PluggableMemoization [f cache] cache/CacheProtocol (has? [_ item] (cache/has? cache item)) (hit [_ item] (PluggableMemoization. f (cache/hit cache item))) (miss [_ item result] (PluggableMemoization. f (cache/miss cache item result))) (evict [_ key] (PluggableMemoization. f (cache/evict cache key))) (lookup [_ item] (cache/lookup cache item nil)) (lookup [_ item not-found] (cache/lookup cache item (delay not-found))) (seed [_ base] (PluggableMemoization. f (cache/seed cache (derefable-seed base)))) Object (toString [_] (str cache))) | |
Returns a function's argument transformer. | (defn- args-fn [x] (or (::args-fn (meta x)) identity)) |
The basic hit/miss logic for the cache system based on | (defn- through* [cache f args item] (cache/through (fn [f _] (d-lay #(f args))) #(apply f %) cache item)) |
Given a function, an atom containing a (pluggable memoization cache), and and cache key function, return a new function that behaves like the original function except it is cached, based on its arguments. | (defn- cached-function [f cache-atom ckey-fn] (fn [& args] (let [ckey (or (ckey-fn args) []) cs (swap! cache-atom through* f args ckey) val (cache/lookup cs ckey ::not-found)] ;; If `lookup` returns `(delay ::not-found)`, it's likely that ;; we ran into a timing issue where eviction and access ;; are happening at about the same time. Therefore, we retry ;; the `swap!` (potentially several times). ;; ;; metabase.util.memoize currently wraps all of its values in a `delay`. (when val (loop [n 0 v @val] (if (= ::not-found v) (when-let [v' (cache/lookup (swap! cache-atom through* f args ckey) ckey ::not-found)] (when (< n 10) (recur (inc n) @v'))) v)))))) |
Build a pluggable memoized version of a function. Given a function and a (pluggable memoized) cache, and an optional seed (hash map of arguments to return values), return a cached version of that function. If you want to build your own cached function, perhaps with combined caches or customized caches, this is the preferred way to do so now. | (defn memoizer ([f cache] (let [cache (atom (PluggableMemoization. f cache)) ckey-fn (args-fn f)] (cached-function f cache ckey-fn))) ([f cache seed] (let [cache (atom (cache/seed (PluggableMemoization. f cache) (derefable-seed seed))) ckey-fn (args-fn f)] (cached-function f cache ckey-fn)))) |
Works the same as the basic memoization function (i.e.
| (defn lru ([f] (lru f {} :lru/threshold 32)) ([f base] (lru f base :lru/threshold 32)) ([f tkey threshold] (lru f {} tkey threshold)) ([f base key threshold] (assert (= key :lru/threshold) (str "wrong parameter key " key)) (memoizer f (cache/lru-cache-factory {} :threshold threshold) base))) |
Used as a more flexible alternative to Clojure's core The default way to use this function is to simply supply a function
that will be memoized. Additionally, you may also supply a map
of the form If the supplied function has metadata containing an
(memo/memo (with-meta jdbc/execute! {::memo/args-fn rest})) You can access the memoization cache directly via the | (defn memo ([f] (memo f {})) ([f seed] (memoizer f (cache/basic-cache-factory {}) seed))) |