aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/cache
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support/cache')
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb2
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb75
-rw-r--r--activesupport/lib/active_support/cache/redis_cache_store.rb74
3 files changed, 97 insertions, 54 deletions
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index 0812cc34c7..a0f44aac0f 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -121,7 +121,7 @@ module ActiveSupport
fname = URI.encode_www_form_component(key)
if fname.size > FILEPATH_MAX_SIZE
- fname = Digest::MD5.hexdigest(key)
+ fname = ActiveSupport::Digest.hexdigest(key)
end
hash = Zlib.adler32(fname)
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index 50f072388d..cae0d44e7d 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -7,7 +7,6 @@ rescue LoadError => e
raise e
end
-require "digest/md5"
require "active_support/core_ext/marshal"
require "active_support/core_ext/array/extract_options"
@@ -64,7 +63,23 @@ module ActiveSupport
addresses = addresses.flatten
options = addresses.extract_options!
addresses = ["localhost:11211"] if addresses.empty?
- Dalli::Client.new(addresses, options)
+
+ pool_options = {}
+ pool_options[:size] = options[:pool_size] if options[:pool_size]
+ pool_options[:timeout] = options[:pool_timeout] if options[:pool_timeout]
+
+ if pool_options.empty?
+ Dalli::Client.new(addresses, options)
+ else
+ begin
+ require "connection_pool"
+ rescue LoadError => e
+ $stderr.puts "You don't have connection_pool installed in your application. Please add it to your Gemfile and run bundle install"
+ raise e
+ end
+
+ ConnectionPool.new(pool_options) { Dalli::Client.new(addresses, options.merge(threadsafe: false)) }
+ end
end
# Creates a new MemCacheStore object, with the given memcached server
@@ -92,28 +107,6 @@ module ActiveSupport
end
end
- # Reads multiple values from the cache using a single call to the
- # servers for all keys. Options can be passed in the last argument.
- def read_multi(*names)
- options = names.extract_options!
- options = merged_options(options)
-
- keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]
-
- raw_values = @data.get_multi(keys_to_names.keys)
- values = {}
-
- raw_values.each do |key, value|
- entry = deserialize_entry(value)
-
- unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
- values[keys_to_names[key]] = entry.value
- end
- end
-
- values
- end
-
# Increment a cached value. This method uses the memcached incr atomic
# operator and can only be used on values written with the :raw option.
# Calling it on a value not stored with :raw will initialize that value
@@ -122,7 +115,7 @@ module ActiveSupport
options = merged_options(options)
instrument(:increment, name, amount: amount) do
rescue_error_with nil do
- @data.incr(normalize_key(name, options), amount, options[:expires_in])
+ @data.with { |c| c.incr(normalize_key(name, options), amount, options[:expires_in]) }
end
end
end
@@ -135,7 +128,7 @@ module ActiveSupport
options = merged_options(options)
instrument(:decrement, name, amount: amount) do
rescue_error_with nil do
- @data.decr(normalize_key(name, options), amount, options[:expires_in])
+ @data.with { |c| c.decr(normalize_key(name, options), amount, options[:expires_in]) }
end
end
end
@@ -143,18 +136,18 @@ module ActiveSupport
# Clear the entire cache on all memcached servers. This method should
# be used with care when shared cache is being used.
def clear(options = nil)
- rescue_error_with(nil) { @data.flush_all }
+ rescue_error_with(nil) { @data.with { |c| c.flush_all } }
end
# Get the statistics from the memcached servers.
def stats
- @data.stats
+ @data.with { |c| c.stats }
end
private
# Read an entry from the cache.
def read_entry(key, options)
- rescue_error_with(nil) { deserialize_entry(@data.get(key, options)) }
+ rescue_error_with(nil) { deserialize_entry(@data.with { |c| c.get(key, options) }) }
end
# Write an entry to the cache.
@@ -167,13 +160,31 @@ module ActiveSupport
expires_in += 5.minutes
end
rescue_error_with false do
- @data.send(method, key, value, expires_in, options)
+ @data.with { |c| c.send(method, key, value, expires_in, options) }
+ end
+ end
+
+ # Reads multiple entries from the cache implementation.
+ def read_multi_entries(names, options)
+ keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]
+
+ raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) }
+ values = {}
+
+ raw_values.each do |key, value|
+ entry = deserialize_entry(value)
+
+ unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options))
+ values[keys_to_names[key]] = entry.value
+ end
end
+
+ values
end
# Delete an entry from the cache.
def delete_entry(key, options)
- rescue_error_with(false) { @data.delete(key) }
+ rescue_error_with(false) { @data.with { |c| c.delete(key) } }
end
# Memcache keys are binaries. So we need to force their encoding to binary
@@ -183,7 +194,7 @@ module ActiveSupport
key = super.dup
key = key.force_encoding(Encoding::ASCII_8BIT)
key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
- key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
+ key = "#{key[0, 213]}:md5:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250
key
end
diff --git a/activesupport/lib/active_support/cache/redis_cache_store.rb b/activesupport/lib/active_support/cache/redis_cache_store.rb
index 3cf002f67e..af14c28408 100644
--- a/activesupport/lib/active_support/cache/redis_cache_store.rb
+++ b/activesupport/lib/active_support/cache/redis_cache_store.rb
@@ -17,6 +17,7 @@ end
require "digest/sha2"
require "active_support/core_ext/marshal"
+require "active_support/core_ext/hash/transform_values"
module ActiveSupport
module Cache
@@ -47,9 +48,11 @@ module ActiveSupport
reconnect_attempts: 0,
}
- DEFAULT_ERROR_HANDLER = -> (method:, returning:, exception:) {
- logger.error { "RedisCacheStore: #{method} failed, returned #{returning.inspect}: #{e.class}: #{e.message}" } if logger
- }
+ DEFAULT_ERROR_HANDLER = -> (method:, returning:, exception:) do
+ if logger
+ logger.error { "RedisCacheStore: #{method} failed, returned #{returning.inspect}: #{exception.class}: #{exception.message}" }
+ end
+ end
DELETE_GLOB_LUA = "for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end"
private_constant :DELETE_GLOB_LUA
@@ -67,7 +70,7 @@ module ActiveSupport
def write_entry(key, entry, options)
if options[:raw] && local_cache
- raw_entry = Entry.new(entry.value.to_s)
+ raw_entry = Entry.new(serialize_entry(entry, raw: true))
raw_entry.expires_at = entry.expires_at
super(key, raw_entry, options)
else
@@ -78,7 +81,7 @@ module ActiveSupport
def write_multi_entries(entries, options)
if options[:raw] && local_cache
raw_entries = entries.map do |key, entry|
- raw_entry = Entry.new(entry.value.to_s)
+ raw_entry = Entry.new(serialize_entry(entry, raw: true))
raw_entry.expires_at = entry.expires_at
end.to_h
@@ -184,7 +187,11 @@ module ActiveSupport
# fetched values.
def read_multi(*names)
if mget_capable?
- read_multi_mget(*names)
+ instrument(:read_multi, names, options) do |payload|
+ read_multi_mget(*names).tap do |results|
+ payload[:hits] = results.keys
+ end
+ end
else
super
end
@@ -226,7 +233,9 @@ module ActiveSupport
# Failsafe: Raises errors.
def increment(name, amount = 1, options = nil)
instrument :increment, name, amount: amount do
- redis.incrby normalize_key(name, options), amount
+ failsafe :increment do
+ redis.incrby normalize_key(name, options), amount
+ end
end
end
@@ -240,7 +249,9 @@ module ActiveSupport
# Failsafe: Raises errors.
def decrement(name, amount = 1, options = nil)
instrument :decrement, name, amount: amount do
- redis.decrby normalize_key(name, options), amount
+ failsafe :decrement do
+ redis.decrby normalize_key(name, options), amount
+ end
end
end
@@ -296,12 +307,23 @@ module ActiveSupport
end
end
+ def read_multi_entries(names, _options)
+ if mget_capable?
+ read_multi_mget(*names)
+ else
+ super
+ end
+ end
+
def read_multi_mget(*names)
options = names.extract_options!
options = merged_options(options)
keys = names.map { |name| normalize_key(name, options) }
- values = redis.mget(*keys)
+
+ values = failsafe(:read_multi_mget, returning: {}) do
+ redis.mget(*keys)
+ end
names.zip(values).each_with_object({}) do |(name, value), results|
if value
@@ -317,7 +339,7 @@ module ActiveSupport
#
# Requires Redis 2.6.12+ for extended SET options.
def write_entry(key, entry, unless_exist: false, raw: false, expires_in: nil, race_condition_ttl: nil, **options)
- value = raw ? entry.value.to_s : serialize_entry(entry)
+ serialized_entry = serialize_entry(entry, raw: raw)
# If race condition TTL is in use, ensure that cache entries
# stick around a bit longer after they would have expired
@@ -326,15 +348,15 @@ module ActiveSupport
expires_in += 5.minutes
end
- failsafe :write_entry do
+ failsafe :write_entry, returning: false do
if unless_exist || expires_in
modifiers = {}
modifiers[:nx] = unless_exist
modifiers[:px] = (1000 * expires_in.to_f).ceil if expires_in
- redis.set key, value, modifiers
+ redis.set key, serialized_entry, modifiers
else
- redis.set key, value
+ redis.set key, serialized_entry
end
end
end
@@ -351,7 +373,7 @@ module ActiveSupport
if entries.any?
if mset_capable? && expires_in.nil?
failsafe :write_multi_entries do
- redis.mapped_mset(entries)
+ redis.mapped_mset(serialize_entries(entries, raw: options[:raw]))
end
else
super
@@ -361,12 +383,12 @@ module ActiveSupport
# Truncate keys that exceed 1kB.
def normalize_key(key, options)
- truncate_key super
+ truncate_key super.b
end
def truncate_key(key)
if key.bytesize > max_key_bytesize
- suffix = ":sha2:#{Digest::SHA2.hexdigest(key)}"
+ suffix = ":sha2:#{::Digest::SHA2.hexdigest(key)}"
truncate_at = max_key_bytesize - suffix.bytesize
"#{key.byteslice(0, truncate_at)}#{suffix}"
else
@@ -374,15 +396,25 @@ module ActiveSupport
end
end
- def deserialize_entry(raw_value)
- if raw_value
- entry = Marshal.load(raw_value) rescue raw_value
+ def deserialize_entry(serialized_entry)
+ if serialized_entry
+ entry = Marshal.load(serialized_entry) rescue serialized_entry
entry.is_a?(Entry) ? entry : Entry.new(entry)
end
end
- def serialize_entry(entry)
- Marshal.dump(entry)
+ def serialize_entry(entry, raw: false)
+ if raw
+ entry.value.to_s
+ else
+ Marshal.dump(entry)
+ end
+ end
+
+ def serialize_entries(entries, raw: false)
+ entries.transform_values do |entry|
+ serialize_entry entry, raw: raw
+ end
end
def failsafe(method, returning: nil)