Skip to content

Instantly share code, notes, and snippets.

@julienfantin
Created February 4, 2014 15:56
Show Gist options
  • Select an option

  • Save julienfantin/8806365 to your computer and use it in GitHub Desktop.

Select an option

Save julienfantin/8806365 to your computer and use it in GitHub Desktop.
Carmine caching
(ns beta-api.utils.cache
(:require [taoensso.carmine :as car]
[clj-time.coerce :as tc]
[clj-time.core :as time]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Redis
(def conn-spec {:host "127.0.0.1"
:port 6379
:timeout 8000})
(def redis-conn {:pool {}
:spec conn-spec})
(defmacro wcar* [& body] `(car/wcar redis-conn ~@body))
(defn now [] (java.util.Date.))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Entries
;; Note, I'd rather have a deftype here, but nippy only handles basic
;; datatypes, though it has an extension feature, it seems very alpha...
(defn value->entry [x]
{:value x
:created-at (now)})
(defn entry->value [e]
(:value e))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Keys to ids
(def root-key "cache-")
(defn ensure-seq [x]
(when x (if (coll? x) x (list x))))
(defn key->str [x]
(cond (string? x) x
(keyword? x) (name x)
:else (str x)))
(defn key->id [x]
(->> x
ensure-seq
(map key->str)
(clojure.string/join "_")
(str root-key)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Policies
(defprotocol CachePolicy
"Returns the number of seconds until expiration."
(expiration [this]))
(defn until-date [^java.util.Date date]
"Returns a CachePolicy that will expire after the given date."
(reify CachePolicy
(expiration [_]
(time/in-seconds
(time/interval (tc/to-date-time (now))
(tc/to-date-time date))))))
(defn for-period [period t]
"Returns a CachePolicy that will be valid for the given clj-time
PeriodType."
(reify CachePolicy
(expiration [_]
(time/in-seconds
(time/interval (tc/to-date-time (now))
(tc/to-date-time (time/plus (tc/to-date-time (now))
(period t))))))))
(defn for-minutes [^Integer minutes]
(for-period time/minutes minutes))
(defn for-seconds [^Integer seconds]
(for-period time/seconds seconds))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Cache accessors
(defn cache-get [^clojure.lang.Named key policy]
(when-let [entry (wcar* (car/get (key->id key)))]
(entry->value entry)))
(defn cache-set [^clojure.lang.Named key policy value]
(when-let [id (key->id key)]
(and (wcar*
(car/set id (value->entry value))
(car/expire id (expiration policy)))
value)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Cache maintenance
(defn cleanup
([] (cleanup nil))
([k]
(let [ks (wcar* (car/keys (str (key->id k) "*")))]
(when-not (empty? ks)
(wcar* (apply car/del ks))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Public API
(defmacro with-cache
[key policy & body]
`(or (cache-get ~key ~policy)
(cache-set ~key ~policy (do ~@body))))
(comment
(with-cache :spikes (for-seconds 5)
(println "Side effects")
42)
(with-cache [:spikes 1] (for-seconds 5)
(println "Side effects")
42))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment