A library to boost performances of Clojure web applications with off-heap serverside caching. Serverside caching is uniquely positionated for effective caching it does NOT require all clients to implement caching directives on the client side. The cached payload is stored off-heap and on disk so that it doesn't put more pressure on the Garbage Collector. Since the cache data is stored on disk it avoid the problem of a cold-restart with and empty cache. The caching logic can be completely customized to tailor your needs.
In order to use the library add the dependency to your project.clj
[com.brunobonacci/ring-boost "0.1.5"]
Then require the namespace:
(ns foo.bar
(:require [com.brunobonacci.ring-boost :as boost]))
(def boost-config
{:enabled true
:profiles
[{:profile :slow-changing-resources
:match [:and
[:uri :starts-with? "/cacheable/request/path"]
[:request-method = :get]]
;; cache for 30 sec
:cache-for 30}]})
;; add ring-boost as last middleware
(def app
(-> app-routes
(wrap-json-response)
(wrap-json-body {:keywords? true})
;; add ring-boost
(boost/ring-boost boost-config)))
Here is a description of the configurable options:
{;; Whether the ring-boost cache is enabled or not.
;; when not enabled the handler will behave as if
;; the ring-boost wasn't there at all.
:enabled true
;; cache storage configuration
;; more options available here:
;; https://github.com/BrunoBonacci/clj-sophia#configuration
:storage {:sophia.path "/tmp/ring-boost-cache"}
;; caching profiles. Profiles are evaluated in order
;; the first matching is used as configuration for
;; the caching strategy.
;; If none match, then the request is considered
;; not cacheable.
:profiles [
{;; whether or not this profile is enabled
;; when disabled it won't be considered at all
:enabled true
;; profile name is (REQUIRED)
:profile :this-profile-name
;; match specification (REQUIRED)
;; for more info on how to construct matching predicates
;; see: https://github.com/BrunoBonacci/where
;; you can provide a custom matching function
;; see: `:matcher`
:match [:and
[:uri :starts-with? "/something/cacheable/"]
[:request-method = :get]]
;; by default it takes the matching predicate defined
;; in `:match` and construct a predicate function which
;; takes a request in input and return whether the
;; request matches this profile or not.
;; You can provide a custom predicate function
:matcher (fn [req] true/false)
;; the duration in seconds for how long ring-boost should
;; serve the item from the cache if present
;; use `:forever` to cache immutable responses
;; default 0
:cache-for 0
;; if the request match this profile, then a key
;; must be construct for looking up the key in the cache.
;; `:keys` provides a list of keys present in the request
;; which will be concatenated to construct a key to lookup
;; and store the response from/into the cache.
;; To retrieve nested keys use a nested vector:
;; such as: [:uri [:headers "content-type"] :query-string]
;; you can provide a custom key function see: `:key-maker`
:keys [:uri :request-method :server-name
:server-port :query-string :body-fingerprint]
;; It is a function that takes a request in input
;; and returns a key for the cache lookup/store.
;; by default it takes the a list of attributes available
;; in the request defined in `:keys` and concatenates
;; them to produce a string
:key-maker (fn [req] (:uri req))
}
]
;; For ADVANCED use only.
;; sequence of processing function for this boost configuration
;; unless specified differently in a caching profile
;; this one will be used.
:processor-seq
[{:name :lift-request }
{:name :cacheable-profilie :call cacheable-profilie}
{:name :request-body-fingerprint :call request-body-fingerprint}
{:name :cache-lookup :call cache-lookup }
{:name :is-cache-expired? :call is-cache-expired? }
{:name :is-skip-cache? :call is-skip-cache? }
{:name :fetch-response :call fetch-response }
{:name :response-cacheable? :call response-cacheable?}
{:name :response-body-normalize :call response-body-normalize}
{:name :add-cache-headers :call add-cache-headers }
{:name :cache-store! :call cache-store! }
{:name :track-cache-metrics :call track-cache-metrics}
{:name :update-cache-stats :call update-cache-stats}
{:name :fetch-cache-stats :call fetch-cache-stats }
{:name :debug-headers :call debug-headers }
{:name :return-response :call return-response }]
}
For a full example see: Fibonacci as service example
ring-boost
supports a number of request headers to handle specific
situations or improve debugging the cache behaviour. Here there is a
list with a small description.
Header | Value | Description |
---|---|---|
X-CACHE-DEBUG |
(present/not-present) | When this header is present the response will |
contains additional headers with the cache | ||
profile and caching statistics. | ||
X-CACHE-SKIP |
(present/not-present) | When this header is present the response will |
be returned from the processing handler and not | ||
from the cache. |
If you want to see ring-boost
debugging headers add the following
request header x-cache-debug: 1
$ curl -is -H 'x-cache-debug: 1' http://localhost:3000/
HTTP/1.1 200 OK
ETag: 30647e5b994dc46db920c54bfebfe5f0
X-CACHE: RING-BOOST/v0.1.3
X-RING-BOOST-CACHE: CACHE-HIT
X-RING-BOOST-CACHE-PROFILE: :cache-profile-name
X-RING-BOOST-CACHE-STATS1: 11/3/0
X-RING-BOOST-CACHE-STATS2: 45/20/5
Where:
X-CACHE
: returns the cache name and version. egRING-BOOST/v0.1.3
X-RING-BOOST-CACHE
: whether is aCACHE-HIT
or aCACHE-MISS
X-RING-BOOST-CACHE-PROFILE
: the name of the cache profile defined in the config which was used for this request.X-RING-BOOST-CACHE-STATS1
: The cache statistic for the cache key (typically URL and more, see:keys
and:key-maker
) in the following formathit/miss/not-cacheable
.X-RING-BOOST-CACHE-STATS2
: The cache statistic for the cache profile (All URLs matching this profile) in the following formathit/miss/not-cacheable
.
NOTE: Please note that stats are updated asynchronously, therefore the values returned are eventually consistent. Furthermore some stats updates might be lost in event of a JVM crash.
If you want to ensure that the handler is called for a particular
request and that the cache value isn't returned if present then adding
the following request header x-cache-skip: 1
. Note that once a
fresher value is returned from the handler it will be stored in the
cache.
The following metrics are tracked via TrackIt
ring_boost.profile.<profile>.hit_rate
ring_boost.profile.<profile>.miss_rate
ring_boost.profile.<profile>.not_cacheable
ring_boost.cache.lookup
ring_boost.cache.store
ring_boost.stats.update
Additionally all clj-sophia engine metrics can be inspected and tracked as well. All metrics can be pushed to a number of different systems such as: Console, Ganglia, Graphite, Statsd, Infuxdb, Reimann and NewRelic.
To enable reporting of these metrics please see TrackIt documentation.
See what the users think of ring-boost
Copyright © 2018 Bruno Bonacci - Distributed under the Apache License v 2.0