-
-
Save NigelThorne/135161 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
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