Created
September 14, 2010 00:17
-
-
Save trak3r/578303 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
# back-port the bypass_local_cache feature from activesupport 3.0.0 | |
module ActiveSupport | |
module Cache | |
class MemoryStore | |
def bypass_local_cache | |
yield | |
end | |
end | |
module Strategy | |
module LocalCache | |
protected | |
def bypass_local_cache | |
save_cache = Thread.current[thread_local_key] | |
begin | |
Thread.current[thread_local_key] = nil | |
yield | |
ensure | |
Thread.current[thread_local_key] = save_cache | |
end | |
end | |
end | |
end | |
end | |
end | |
module WaitForCache | |
class Lock | |
def initialize(thread_id_override_for_testing = nil) | |
@thread_id = thread_id_override_for_testing || Process.pid | |
end | |
def mine? | |
Process.pid == @thread_id | |
end | |
def to_s | |
@thread_id.to_s | |
end | |
end | |
def log(key, msg) | |
Rails.logger.add(3, "WaitForCache(#{Process.pid}): #{msg}") | |
end | |
def wait_for_cache(key, attempt_count = 0, &block) | |
# get value from cache | |
value = Rails.cache.send(:bypass_local_cache) { Rails.cache.read(key) } | |
if value.nil? | |
# value is nil | |
if 0 < attempt_count | |
# something is f'ed up; the cache might be broken | |
log(key,"*** Failed to lock") | |
block.call | |
else | |
# "add" my lock object into the cache | |
log(key,"Locking") | |
Rails.cache.write(key, Lock.new, :unless_exist => true) | |
# (will fail if somebody beats me, and that's OK) | |
# recurse | |
wait_for_cache(key, 1+attempt_count, &block) | |
end | |
elsif value.kind_of?(Lock) | |
# value is a lock object | |
if value.mine? | |
# lock object is mine | |
# data = yield block | |
log(key,"Calling") | |
value = block.call | |
# "set" data into cache (replaces the lock object) | |
log(key,"Caching") | |
Rails.cache.write(key, value) | |
# return query data (or recurse if you want to be pure) | |
wait_for_cache(key, attempt_count, &block) | |
else | |
# lock object is somebody else's | |
if 5 > attempt_count | |
# i've not retried three times yet | |
# sleep for a little bit | |
log(key,"Sleeping (locked by #{value})") | |
sleep(1.0) # seconds | |
# increment retry count | |
# recurse | |
wait_for_cache(key, 1 + attempt_count, &block) | |
else | |
# this is the third time i've tried | |
# (they are taking too long or might have died) | |
# replace lock object with nil | |
log(key,"*** Killing") | |
Rails.cache.write(key, nil) | |
# reset retry count | |
# recurse | |
wait_for_cache(key, 0, &block) | |
end | |
end | |
else | |
# value is data | |
# return data | |
log(key,"Got cached data") | |
value | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment