Skip to content

Instantly share code, notes, and snippets.

@dalezak
Last active March 14, 2023 21:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dalezak/086ccb6e4f2beb868c5bd0d5e9b9640a to your computer and use it in GitHub Desktop.
Save dalezak/086ccb6e4f2beb868c5bd0d5e9b9640a to your computer and use it in GitHub Desktop.
Rails Cacheable Concern
# Rails concern that tracks and purges cache keys to avoid using Rails.cache.delete_matched since its very costly on Redis.
#
# @countries = Country.query_cached('countries:index', "limit:#{@limit}", "offset:#{@offset}") do
# query.limit(@limit).offset(@offset).all.to_a
# end
module Cacheable
extend ActiveSupport::Concern
included do
before_save :purge_cache
before_destroy :purge_cache
end
class_methods do
def find_cached(id)
Rails.cache.fetch(find_cached_key(id), skip_nil: true) do
find(id)
end
end
def find_cached_key(id)
"#{model_name.cache_key}/#{id}/find_cached"
end
def query_cached(*keys, &block)
unless query_cached_keys.include?(query_cached_key(keys))
query_cached_keys << query_cached_key(keys)
Rails.cache.write(query_cached_keys_key, query_cached_keys)
end
Rails.cache.fetch(query_cached_key(keys), skip_nil: true) do
block.call
end
end
def query_cached_key(*keys)
"#{model_name.cache_key}/query_cached/#{keys.join("/")}"
end
def query_cached_keys_key
"#{model_name.cache_key}/query_cached_keys"
end
def query_cached_keys
@query_cached_keys ||= Rails.cache.read(query_cached_keys_key, skip_nil: true) || []
end
def purge_cache
find_each do |record|
record.purge_cache
end
end
end
def find_cached_key
@find_cached_key ||= self.class.find_cached_key(id)
end
def purge_cache
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
Rails.cache.delete(self.class.find_cached_key(id))
if self.class.query_cached_keys.present?
# TODO use Rails.cache.delete_multi after upgrading to Rails 6.1
# Rails.cache.delete_multi(self.class.query_cached_keys)
self.class.query_cached_keys.each do |query_cached_key|
Rails.cache.delete(query_cached_key)
end
Rails.cache.delete(self.class.query_cached_keys_key)
Rails.cache.delete_match
end
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
Rails.logger.debug "#{self.class.name}#purge_cache #{ending - starting} seconds"
return true
end
def query_cached(*keys, &block)
Rails.cache.fetch("#{self.cache_key_with_version}/#{keys.join("/")}") do
block.call
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment