Instrumenting cache stores.
This commit is contained in:
parent
09f798ba18
commit
4215e9ab93
@ -107,16 +107,14 @@ def self.expand_cache_key(key, namespace = nil)
|
|||||||
class Store
|
class Store
|
||||||
cattr_accessor :logger
|
cattr_accessor :logger
|
||||||
|
|
||||||
attr_reader :silence, :logger_off
|
attr_reader :silence
|
||||||
|
alias :silence? :silence
|
||||||
|
|
||||||
def silence!
|
def silence!
|
||||||
@silence = true
|
@silence = true
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
alias silence? silence
|
|
||||||
alias logger_off? logger_off
|
|
||||||
|
|
||||||
# Fetches data from the cache, using the given key. If there is data in
|
# Fetches data from the cache, using the given key. If there is data in
|
||||||
# the cache with the given key, then that data is returned.
|
# the cache with the given key, then that data is returned.
|
||||||
#
|
#
|
||||||
@ -157,26 +155,13 @@ def silence!
|
|||||||
# cache.fetch("foo") # => "bar"
|
# cache.fetch("foo") # => "bar"
|
||||||
# sleep(6)
|
# sleep(6)
|
||||||
# cache.fetch("foo") # => nil
|
# cache.fetch("foo") # => nil
|
||||||
def fetch(key, options = {})
|
def fetch(key, options = {}, &block)
|
||||||
@logger_off = true
|
|
||||||
if !options[:force] && value = read(key, options)
|
if !options[:force] && value = read(key, options)
|
||||||
@logger_off = false
|
|
||||||
log("hit", key, options)
|
|
||||||
value
|
value
|
||||||
elsif block_given?
|
elsif block_given?
|
||||||
@logger_off = false
|
result = instrument(:generate, key, options, &block)
|
||||||
log("miss", key, options)
|
write(key, result, options)
|
||||||
|
result
|
||||||
value = nil
|
|
||||||
ms = Benchmark.ms { value = yield }
|
|
||||||
|
|
||||||
@logger_off = true
|
|
||||||
write(key, value, options)
|
|
||||||
@logger_off = false
|
|
||||||
|
|
||||||
log('write (will save %.2fms)' % ms, key, nil)
|
|
||||||
|
|
||||||
value
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -191,8 +176,8 @@ def fetch(key, options = {})
|
|||||||
# For example, FileStore supports the +:expires_in+ option, which
|
# For example, FileStore supports the +:expires_in+ option, which
|
||||||
# makes the method return nil for cache items older than the specified
|
# makes the method return nil for cache items older than the specified
|
||||||
# period.
|
# period.
|
||||||
def read(key, options = nil)
|
def read(key, options = nil, &block)
|
||||||
log("read", key, options)
|
instrument(:read, key, options, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Writes the given value to the cache, with the given key.
|
# Writes the given value to the cache, with the given key.
|
||||||
@ -210,20 +195,20 @@ def read(key, options = nil)
|
|||||||
# cache.read("foo") # => "bar"
|
# cache.read("foo") # => "bar"
|
||||||
# sleep(6)
|
# sleep(6)
|
||||||
# cache.read("foo") # => nil
|
# cache.read("foo") # => nil
|
||||||
def write(key, value, options = nil)
|
def write(key, value, options = nil, &block)
|
||||||
log("write", key, options)
|
instrument(:write, key, options, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(key, options = nil)
|
def delete(key, options = nil, &block)
|
||||||
log("delete", key, options)
|
instrument(:delete, key, options, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_matched(matcher, options = nil)
|
def delete_matched(matcher, options = nil, &block)
|
||||||
log("delete matched", matcher.inspect, options)
|
instrument(:delete_matched, matcher.inspect, options, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def exist?(key, options = nil)
|
def exist?(key, options = nil, &block)
|
||||||
log("exist?", key, options)
|
instrument(:exist?, key, options, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def increment(key, amount = 1)
|
def increment(key, amount = 1)
|
||||||
@ -247,14 +232,21 @@ def decrement(key, amount = 1)
|
|||||||
private
|
private
|
||||||
def expires_in(options)
|
def expires_in(options)
|
||||||
expires_in = options && options[:expires_in]
|
expires_in = options && options[:expires_in]
|
||||||
|
|
||||||
raise ":expires_in must be a number" if expires_in && !expires_in.is_a?(Numeric)
|
raise ":expires_in must be a number" if expires_in && !expires_in.is_a?(Numeric)
|
||||||
|
|
||||||
expires_in || 0
|
expires_in || 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def instrument(operation, key, options, &block)
|
||||||
|
payload = { :key => key }
|
||||||
|
payload.merge!(options) if options.is_a?(Hash)
|
||||||
|
|
||||||
|
event = ActiveSupport::Orchestra.instrument(:"cache_#{operation}", payload, &block)
|
||||||
|
log("#{operation} (%.1fms)" % event.duration, key, options)
|
||||||
|
event.result
|
||||||
|
end
|
||||||
|
|
||||||
def log(operation, key, options)
|
def log(operation, key, options)
|
||||||
logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !silence? && !logger_off?
|
logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !silence?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -16,8 +16,7 @@ def initialize(cache_path)
|
|||||||
# - +:expires_in+ - the number of seconds that this value may stay in
|
# - +:expires_in+ - the number of seconds that this value may stay in
|
||||||
# the cache.
|
# the cache.
|
||||||
def read(name, options = nil)
|
def read(name, options = nil)
|
||||||
super
|
super do
|
||||||
|
|
||||||
file_name = real_file_path(name)
|
file_name = real_file_path(name)
|
||||||
expires = expires_in(options)
|
expires = expires_in(options)
|
||||||
|
|
||||||
@ -25,26 +24,29 @@ def read(name, options = nil)
|
|||||||
File.open(file_name, 'rb') { |f| Marshal.load(f) }
|
File.open(file_name, 'rb') { |f| Marshal.load(f) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Writes a value to the cache.
|
# Writes a value to the cache.
|
||||||
def write(name, value, options = nil)
|
def write(name, value, options = nil)
|
||||||
super
|
super do
|
||||||
ensure_cache_path(File.dirname(real_file_path(name)))
|
ensure_cache_path(File.dirname(real_file_path(name)))
|
||||||
File.atomic_write(real_file_path(name), cache_path) { |f| Marshal.dump(value, f) }
|
File.atomic_write(real_file_path(name), cache_path) { |f| Marshal.dump(value, f) }
|
||||||
value
|
value
|
||||||
|
end
|
||||||
rescue => e
|
rescue => e
|
||||||
logger.error "Couldn't create cache directory: #{name} (#{e.message})" if logger
|
logger.error "Couldn't create cache directory: #{name} (#{e.message})" if logger
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(name, options = nil)
|
def delete(name, options = nil)
|
||||||
super
|
super do
|
||||||
File.delete(real_file_path(name))
|
File.delete(real_file_path(name))
|
||||||
|
end
|
||||||
rescue SystemCallError => e
|
rescue SystemCallError => e
|
||||||
# If there's no cache, then there's nothing to complain about
|
# If there's no cache, then there's nothing to complain about
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_matched(matcher, options = nil)
|
def delete_matched(matcher, options = nil)
|
||||||
super
|
super do
|
||||||
search_dir(@cache_path) do |f|
|
search_dir(@cache_path) do |f|
|
||||||
if f =~ matcher
|
if f =~ matcher
|
||||||
begin
|
begin
|
||||||
@ -55,11 +57,13 @@ def delete_matched(matcher, options = nil)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def exist?(name, options = nil)
|
def exist?(name, options = nil)
|
||||||
super
|
super do
|
||||||
File.exist?(real_file_path(name))
|
File.exist?(real_file_path(name))
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def real_file_path(name)
|
def real_file_path(name)
|
||||||
|
@ -54,8 +54,9 @@ def read_multi(*keys)
|
|||||||
end
|
end
|
||||||
|
|
||||||
def read(key, options = nil) # :nodoc:
|
def read(key, options = nil) # :nodoc:
|
||||||
super
|
super do
|
||||||
@data.get(key, raw?(options))
|
@data.get(key, raw?(options))
|
||||||
|
end
|
||||||
rescue MemCache::MemCacheError => e
|
rescue MemCache::MemCacheError => e
|
||||||
logger.error("MemCacheError (#{e}): #{e.message}")
|
logger.error("MemCacheError (#{e}): #{e.message}")
|
||||||
nil
|
nil
|
||||||
@ -69,22 +70,24 @@ def read(key, options = nil) # :nodoc:
|
|||||||
# - <tt>:expires_in</tt> - the number of seconds that this value may stay in
|
# - <tt>:expires_in</tt> - the number of seconds that this value may stay in
|
||||||
# the cache. See ActiveSupport::Cache::Store#write for an example.
|
# the cache. See ActiveSupport::Cache::Store#write for an example.
|
||||||
def write(key, value, options = nil)
|
def write(key, value, options = nil)
|
||||||
super
|
super do
|
||||||
method = options && options[:unless_exist] ? :add : :set
|
method = options && options[:unless_exist] ? :add : :set
|
||||||
# memcache-client will break the connection if you send it an integer
|
# memcache-client will break the connection if you send it an integer
|
||||||
# in raw mode, so we convert it to a string to be sure it continues working.
|
# in raw mode, so we convert it to a string to be sure it continues working.
|
||||||
value = value.to_s if raw?(options)
|
value = value.to_s if raw?(options)
|
||||||
response = @data.send(method, key, value, expires_in(options), raw?(options))
|
response = @data.send(method, key, value, expires_in(options), raw?(options))
|
||||||
response == Response::STORED
|
response == Response::STORED
|
||||||
|
end
|
||||||
rescue MemCache::MemCacheError => e
|
rescue MemCache::MemCacheError => e
|
||||||
logger.error("MemCacheError (#{e}): #{e.message}")
|
logger.error("MemCacheError (#{e}): #{e.message}")
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(key, options = nil) # :nodoc:
|
def delete(key, options = nil) # :nodoc:
|
||||||
super
|
super do
|
||||||
response = @data.delete(key, expires_in(options))
|
response = @data.delete(key, expires_in(options))
|
||||||
response == Response::DELETED
|
response == Response::DELETED
|
||||||
|
end
|
||||||
rescue MemCache::MemCacheError => e
|
rescue MemCache::MemCacheError => e
|
||||||
logger.error("MemCacheError (#{e}): #{e.message}")
|
logger.error("MemCacheError (#{e}): #{e.message}")
|
||||||
false
|
false
|
||||||
@ -94,8 +97,10 @@ def exist?(key, options = nil) # :nodoc:
|
|||||||
# Doesn't call super, cause exist? in memcache is in fact a read
|
# Doesn't call super, cause exist? in memcache is in fact a read
|
||||||
# But who cares? Reading is very fast anyway
|
# But who cares? Reading is very fast anyway
|
||||||
# Local cache is checked first, if it doesn't know then memcache itself is read from
|
# Local cache is checked first, if it doesn't know then memcache itself is read from
|
||||||
|
super do
|
||||||
!read(key, options).nil?
|
!read(key, options).nil?
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def increment(key, amount = 1) # :nodoc:
|
def increment(key, amount = 1) # :nodoc:
|
||||||
log("incrementing", key, amount)
|
log("incrementing", key, amount)
|
||||||
|
@ -20,29 +20,34 @@ def initialize
|
|||||||
end
|
end
|
||||||
|
|
||||||
def read(name, options = nil)
|
def read(name, options = nil)
|
||||||
super
|
super do
|
||||||
@data[name]
|
@data[name]
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def write(name, value, options = nil)
|
def write(name, value, options = nil)
|
||||||
super
|
super do
|
||||||
@data[name] = (value.duplicable? ? value.dup : value).freeze
|
@data[name] = (value.duplicable? ? value.dup : value).freeze
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def delete(name, options = nil)
|
def delete(name, options = nil)
|
||||||
super
|
super do
|
||||||
@data.delete(name)
|
@data.delete(name)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def delete_matched(matcher, options = nil)
|
def delete_matched(matcher, options = nil)
|
||||||
super
|
super do
|
||||||
@data.delete_if { |k,v| k =~ matcher }
|
@data.delete_if { |k,v| k =~ matcher }
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def exist?(name,options = nil)
|
def exist?(name,options = nil)
|
||||||
super
|
super do
|
||||||
@data.has_key?(name)
|
@data.has_key?(name)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def clear
|
def clear
|
||||||
@data.clear
|
@data.clear
|
||||||
|
Loading…
Reference in New Issue
Block a user