-
-
Save djmb/bd90a92d0b6cf4a6e44e94049c7352ec to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module ActiveSupport | |
module Cache | |
# A cache that splits calls between multiple caches based on the cache key | |
# | |
# It takes a map of cache names to cache arguments and a splitter lambda | |
# that chooses the cache to use based on the cache key. | |
# | |
# Configure with something like: | |
# config.cache_store = [ | |
# :splitter_cache_store, | |
# caches: { redis: :redis_cache_store, solid_cache: :solid_cache_store }, | |
# splitter: ->(key) ::Digest::MD5.digest(key).unpack1("L>") % 100 < 50 ? :solid_cache : :redis | |
# ] | |
class SplitterCacheStore < Store | |
KEY_METHODS = [ :fetch, :read, :write, :delete, :exist?, :increment, :decrement ] | |
NO_KEY_METHODS = [ :silence!, :mute, :delete_matched, :cleanup, :clear ] | |
def self.supports_cache_versioning? | |
true | |
end | |
attr_reader :caches, :splitter | |
def initialize(options) | |
@caches = options.delete(:caches).to_h do |name, *cache_args| | |
[ name, ActiveSupport::Cache.lookup_store(*cache_args) ] | |
end | |
@splitter = options.delete(:splitter) | |
super(options) | |
end | |
KEY_METHODS.each do |method| | |
define_method method do |*args, **kwargs, &block| | |
choose_cache(args.first).public_send(method, *args, **kwargs, &block) | |
end | |
end | |
NO_KEY_METHODS.each do |method| | |
define_method method do |*args, **kwargs, &block| | |
caches.values.each do |cache| | |
cache.public_send(method, *args, **kwargs, &block) | |
end | |
end | |
end | |
def read_multi(*names) | |
options = names.extract_options! | |
cache_to_names(names).each_with_object({}) do |(cache, names), results| | |
results.merge!(cache.read_multi(*names, **options)) | |
end | |
end | |
def write_multi(hash, options = nil) | |
per_cache_writes = {} | |
hash.each do |name, value| | |
cache = choose_cache(name) | |
per_cache_writes[cache] ||= {} | |
per_cache_writes[cache][name] = value | |
end | |
per_cache_writes.each do |cache, hash| | |
cache.write_multi(hash, options) | |
end | |
end | |
def fetch_multi(*names, &block) | |
raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block | |
options = names.extract_options! | |
cache_to_names(names).each_with_object({}) do |(cache, names), results| | |
results.merge!(cache.fetch_multi(*names, **options, &block)) | |
end | |
end | |
def delete_multi(names, options = nil) | |
cache_to_names(names).each_with_object(0) do |(cache, names), results| | |
results += cache.delete_multi(names, options) | |
end | |
end | |
private | |
def choose_cache(key) | |
caches[splitter.call(normalize_key(key))] | |
end | |
def cache_to_names(names) | |
names.each_with_object({}) do |name, results| | |
(results[choose_cache(name)] ||= []) << name | |
end | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment