base on guanxi.me
author: Francis Jiang
def smart_fetch(name, options, &blk)
options = HashWithIndifferentAccess.new(options)
in_cache = Rails.cache.fetch(name)
return in_cache if in_cache
val = yield
Rails.cache.write(name, val, options)
return val
end
loop do
proc = Proc.new{ 1+3;sleep 5 }
puts smart_fetch("test", {:expires_in => 3.seconds}, &proc)
sleep 1
end
def generate_cache_key(cache_key = nil, expires_in = nil, options = {})
cache_key_suffix ||= "_#{Rails.env}"
if expires_in.present?
"#{cache_key}_expires_in_#{expires_in}_#{cache_key_suffix}"
else
"#{cache_key}_#{cache_key_suffix}"
end
end
# cache_value
# if with the cache_key have no entry cache the value from proc.call
# if arg[:refresh] present cache the value from proc.call
# elsif time is overtake arg[:fresh_countdown] cache the value from proc.call and run it with an new thread
# else read from cache server
# Utility.cache_value({cache_key: "#{User.name.underscore}", expires_in: 10.minutes, fresh_countdown: 60}) { User.count }
def cache_value(arg = {cache_key: nil, expires_in: 10.minutes, fresh_countdown: 60})
arg = HashWithIndifferentAccess.new(arg)
expires_in = arg[:expires_in] || 10.minutes
fresh_countdown = arg[:fresh_countdown] || 1.minutes
user_cache_key = arg[:cache_key]
# FIXME cache key will not invoke expires time
cache_key = generate_cache_key(user_cache_key)
refresh = arg[:refresh]
if cache_key.present? and expires_in.present?
cached_value = Rails.cache.read(cache_key)
if refresh or cached_value.nil?
value = yield
Rails.cache.write(cache_key, value, :expires_in => expires_in)
cached_value = value
elsif should_fresh?(cache_key, {:expires_in => expires_in}, fresh_countdown)
Thread.new() {
Rails.cache.write(cache_key, value, :expires_in => expires_in)
}
end
cached_value
else
raise "Argument Missing cache_key: #{cache_key}, expires_in: #{expires_in}"
end
end
# through common cache options auto renewal within a fresh_countdown times
# return a time instance told us when should refresh an cached entry
def should_refresh_at(cache_key, options = {}, fresh_countdown = 60)
options = HashWithIndifferentAccess.new(options)
if options[:expires_at].respond_to?(:to_time)
_expires_at = options[:expires_at].to_time
_expires_in = _expires_at - Time.now
end
if options[:expires_in].present?
_expires_in = options[:expires_in].to_i
_expires_at = Time.at(Time.now + _expires_in)
end
_should_expires_in = _expires_in - fresh_countdown + 2
return if _expires_at.nil? or _expires_in.nil? or (_should_expires_in < 0)
cache_key = "#{cache_key}_should_expires_in_#{_should_expires_in}"
in_cache = Rails.cache.read(cache_key)
if in_cache.nil?
Rails.cache.write(cache_key, _expires_at, {expires_in: _should_expires_in})
_expires_at
else
in_cache
end
end
# if time is overtake fresh_countdown return true else false
# otherwise return true
# loop do
# puts Utility.should_fresh?("aa1",{expires_in: 10.seconds},3)
# sleep 1
# end
def should_fresh?(cache_key, options = {}, fresh_countdown = 60)
_expires_at = should_refresh_at(cache_key, options, fresh_countdown)
if _expires_at.present?
(_expires_at - Time.now) < fresh_countdown
else
true
end
end
# just pick the value `cache_value` cached
def get_cache_value(cache_key = nil)
if cache_key.present?
user_cache_key = cache_key
cache_key = generate_cache_key(user_cache_key)
Rails.cache.read(cache_key)
end
end
# lazy cache
# when value present update cached entry
# else read from cache server
def lazy_cacher(arg = {cache_key: nil, value: nil, expires_in: 1.days})
expires_in = arg[:expires_in]
expires_in ||= 1.days
user_cache_key = arg[:cache_key]
cache_key = generate_cache_key(user_cache_key, expires_in)
value = arg[:value]
destroy = arg[:destroy]
if cache_key.present? and expires_in.present?
cache_key = "#{arg[:cache_key]}_expires_in_#{expires_in}_#{Rails.env}"
if value.present?
Rails.cache.write(cache_key, value, :expires_in => expires_in)
else
value = Rails.cache.read(cache_key)
Rails.cache.delete(cache_key) if destroy
end
value
else
raise "Argument Missing cache_key: #{cache_key}, value: #{value}, expires_in: #{expires_in}"
end
end
Utility.should_refresh_at("rand", {expires_in: 10.seconds}, 10)
# Utility.cache_value({cache_key: "rand", expires_in: 10.seconds, fresh_countdown: 3}) { rand(1..8) }
loop do
puts Utility.should_fresh?("aa1",{expires_in: 10.seconds},3)
sleep 1
end
loop do
puts Utility.cache_value({cache_key: "rand", expires_in: 10.seconds, fresh_countdown: 3}) { sleep 3;rand(1..8) }
sleep 1
end