(ns metabase.lib.cache
#?@(:clj ((:import
[java.util Collections Map WeakHashMap]
[java.util.function Function]))
:cljs ((:require [goog.object :as gobject])))) | |
#?(:clj
(def ^:private atom-function
(reify
Function
(apply [_this _arg]
(atom {}))))) | |
Caching wrapper for use in [[side-channel-cache]]. CLJS Attaches the cache to the CLJ Attaches the cache to the host's metadata, if present. See [[attach-query-cache]]. Does nothing if that
key is not found. The outer cache is a synchronized | (defn- atomic-map-cache-fn
#?(:cljs ([^js host]
(if-let [cache (.-__mbcache host)]
cache
(set! (.-__mbcache host) (atom {}))))
:clj ([host]
(when-let [^Map outer-cache (-> host meta :lib/__cache)]
(.computeIfAbsent outer-cache host atom-function))))
([cache subkey _x] (get @cache subkey))
([cache subkey _x value] (swap! cache assoc subkey value))) |
Attaches the cache to a newly constructed query. This uses metadata on CLJ and does nothing on CLJS. | #?(:cljs
(defn- js-weak-map-cache-fn
"Caching wrapper for use in [[side-channel-cache*]].
Uses a two-layer cache: the outer layer is a vanilla JS object with string `subkey`s as its keys. The values are
`WeakMap`s, using the input value `x` as the key and holding the cached result.
For example, this works for caching by `:database-id` and then by legacy query object."
([^js host] (if-let [cache (.-__mbcache host)]
cache
(set! (.-__mbcache host) #js {})))
([^js cache subkey x] (when-let [inner-cache (gobject/get cache subkey)]
(.get inner-cache x)))
([^js cache subkey x value] (let [inner-cache (gobject/setWithReturnValueIfNotSet cache subkey #(js/WeakMap.))]
(.set inner-cache x value)))))
(defn attach-query-cache
[query]
#?(:cljs query
:clj (vary-meta query (fn [mmeta]
(cond-> mmeta
(not (contains? mmeta :lib/__cache))
(assoc :lib/__cache (Collections/synchronizedMap (WeakHashMap.)))))))) |
Removes the cache from the given query. Does nothing if no cache is present. | (defn discard-query-cache
[query]
#?(:cljs query
:clj (when query
(vary-meta query dissoc :lib/__cache)))) |
(CLJS only; this is a pass-through in CLJ.) Attaches a JS property This cache forms a "personal" cache attached to If the If the The caches are passed both If there is no existing value in the cache for Options:
- The optional By default, the cache is an | (defn- side-channel-cache*
[subkey host x f {:keys [cache-fn force?]
:or {cache-fn atomic-map-cache-fn}}]
(if (or force?
(map? host)
#?(:cljs (object? host)))
(if-let [cache (cache-fn host)]
(if-let [cached (cache-fn cache subkey x)]
cached
;; Cache miss - generate the value and cache it.
(let [value (f x)]
(cache-fn cache subkey x value)
value))
(f x))
(f x))) |
Creates a cache on the given See [[atomic-map-cache-fn]] for more details on how the caches are created and stored. The cache is intended to be a "personal" cache attached to the NOTE: The 3-arity uses If there is no existing value at The inner cache is an CLJS Notes If the If the Options:
- | (defn side-channel-cache
([subkey x f]
(side-channel-cache subkey x f false))
([subkey x f force?]
(side-channel-cache* subkey x x f
{:cache-fn atomic-map-cache-fn
:force? force?}))
([subkey host x f opts]
(side-channel-cache* subkey host x f
(merge {:cache-fn atomic-map-cache-fn} opts)))) |