mirror of
https://github.com/penpot/penpot.git
synced 2026-08-12 07:49:05 +00:00
94 lines
3.0 KiB
Clojure
94 lines
3.0 KiB
Clojure
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
;;
|
|
;; Copyright (c) KALEIDOS INC Sucursal en España SL
|
|
|
|
(ns app.util.cache
|
|
"In-memory cache backed by Caffeine"
|
|
(:refer-clojure :exclude [get])
|
|
(:require
|
|
[app.common.schema :as sm]
|
|
[app.common.time :as ct]
|
|
[promesa.exec :as px])
|
|
(:import
|
|
com.github.benmanes.caffeine.cache.Cache
|
|
com.github.benmanes.caffeine.cache.Caffeine
|
|
com.github.benmanes.caffeine.cache.RemovalListener
|
|
com.github.benmanes.caffeine.cache.stats.CacheStats
|
|
java.time.Duration
|
|
java.util.concurrent.Executor
|
|
java.util.function.Function))
|
|
|
|
(set! *warn-on-reflection* true)
|
|
|
|
(defprotocol ICache
|
|
(get [_ k] [_ k load-fn] "get cache entry")
|
|
(invalidate [_] [_ k] "invalidate cache")
|
|
(invalidate-if [_ pred] "invalidate all entries whose value satisfies pred"))
|
|
|
|
(defprotocol ICacheStats
|
|
(stats [_] "get stats"))
|
|
|
|
(defn- create-listener
|
|
[f]
|
|
(reify RemovalListener
|
|
(onRemoval [_ key val cause]
|
|
(when val
|
|
(f key val cause)))))
|
|
|
|
(defn- get-stats
|
|
[^Cache cache]
|
|
(let [^CacheStats stats (.stats cache)]
|
|
{:hit-rate (.hitRate stats)
|
|
:hit-count (.hitCount stats)
|
|
:req-count (.requestCount stats)
|
|
:miss-count (.missCount stats)
|
|
:miss-rate (.missRate stats)}))
|
|
|
|
(defn create
|
|
"Build an in-memory cache. Loads run synchronously on the calling
|
|
thread, so when a load fn throws or returns nil the entry is not
|
|
stored — concurrent loads for the same key still deduplicate."
|
|
[& {:keys [executor on-remove max-size keepalive expire]}]
|
|
(let [cache (as-> (Caffeine/newBuilder) builder
|
|
(if (fn? on-remove) (.removalListener builder (create-listener on-remove)) builder)
|
|
(if executor (.executor builder ^Executor (px/resolve-executor executor)) builder)
|
|
(if keepalive (.expireAfterAccess builder ^Duration (ct/duration keepalive)) builder)
|
|
(if expire (.expireAfterWrite builder ^Duration (ct/duration expire)) builder)
|
|
(if (int? max-size) (.maximumSize builder (long max-size)) builder)
|
|
(.recordStats builder)
|
|
(.build builder))]
|
|
(reify
|
|
ICache
|
|
(get [_ k]
|
|
(.getIfPresent ^Cache cache ^Object k))
|
|
(get [_ k load-fn]
|
|
(.get ^Cache cache
|
|
^Object k
|
|
^Function (reify Function
|
|
(apply [_ k]
|
|
(load-fn k)))))
|
|
(invalidate [_]
|
|
(.invalidateAll ^Cache cache))
|
|
(invalidate [_ k]
|
|
(.invalidate ^Cache cache ^Object k))
|
|
(invalidate-if [_ pred]
|
|
(doseq [[k v] (.asMap ^Cache cache)]
|
|
(when (pred v)
|
|
(.invalidate ^Cache cache ^Object k))))
|
|
|
|
ICacheStats
|
|
(stats [_]
|
|
(get-stats cache)))))
|
|
|
|
(defn cache?
|
|
[o]
|
|
(satisfies? ICache o))
|
|
|
|
(sm/register!
|
|
{:type ::cache
|
|
:pred cache?
|
|
:type-properties
|
|
{:title "cache instance"}})
|