Skip to content

Instantly share code, notes, and snippets.

@sco
Created March 26, 2009 12:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sco/86069 to your computer and use it in GitHub Desktop.
Save sco/86069 to your computer and use it in GitHub Desktop.
require 'memcached'
# Provides a hybrid of memoization and memcacheing. Designed for storing an entire (small)
# table in memory, in every instance of the app. Works well for reference-type tables
# (e.g., Category) which won't ever get big or change often, but are read-heavy. This
# allows you to avoid joins, but also avoid the n+1 queries antipattern.
#
# A pure memoization solution fails when the data *does* change -- all app instances have
# to be restarted for the in-memory caches to be correct. A pure memcached solution solves
# that, but requires a largish amount of data to be retrieved from memcached for every
# request, which is inefficient. With CacheCache, the data is cached both in system memory
# and memcached, but only pulled from memcached when the data changes. In the normal case,
# only a small integer is pulled from memcached for each app request.
#
# At the beginning of every request, call #check_version so that stale data can be
# refreshed. Whenever the underlying data changes, call #flush.
#
class CacheCache
def initialize(memcached, namespace='cache_cache')
@memcached = memcached
@namespace = namespace
@local_cache = {}
@last_version = nil
end
def get_or(key, &block)
memcached_key = [@namespace, key, @last_version || current_version].join
@local_cache[key] ||= @memcached.get_or(memcached_key) do
yield
end
end
def check
if @last_version.nil? || @last_version != current_version
@local_cache = {}
@last_version = current_version
end
end
def flush
@memcached.incr("#{@namespace}/version")
@last_version = nil
ensure
check
end
private
def current_version
@memcached.get("#{@namespace}/version", false).to_i
rescue Memcached::NotFound
v = Time.now.to_i
@memcached.set("#{@namespace}/version", v.to_s, 0, false)
v
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment