class ActiveSupport::Cache::MemcachedStore

A cache store implementation which stores data in Memcached: memcached.org/

MemcachedStore uses memcached gem as backend to connect to Memcached server.

MemcachedStore implements the Strategy::LocalCache strategy which implements an in-memory cache inside of a block.

Constants

ESCAPE_KEY_CHARS

Attributes

read_only[RW]
swallow_exceptions[RW]

Public Class Methods

new(*addresses, **options) click to toggle source
Calls superclass method
# File lib/active_support/cache/memcached_store.rb, line 64
def initialize(*addresses, **options)
  addresses = addresses.flatten
  options[:codec] ||= Codec.new
  @swallow_exceptions = true
  @swallow_exceptions = options.delete(:swallow_exceptions) if options.key?(:swallow_exceptions)

  super(options)

  if addresses.first.is_a?(Memcached)
    @connection = addresses.first
    raise "Memcached::Rails is no longer supported, "\
          "use a Memcached instance instead" if @connection.is_a?(Memcached::Rails)
  else
    mem_cache_options = options.dup
    servers = mem_cache_options.delete(:servers)
    UNIVERSAL_OPTIONS.each { |name| mem_cache_options.delete(name) }
    @connection = Memcached.new([*addresses, *servers], mem_cache_options)
  end
end

Public Instance Methods

append(name, value, options = nil) click to toggle source
# File lib/active_support/cache/memcached_store.rb, line 84
def append(name, value, options = nil)
  return true if read_only
  options = merged_options(options)
  normalized_key = normalize_key(name, options)

  handle_exceptions(return_value_on_error: nil, on_miss: false, miss_exceptions: [Memcached::NotStored]) do
    instrument(:append, name) do
      @connection.append(normalized_key, value)
    end
    true
  end
end
cas(name, options = nil) { |value| ... } click to toggle source
# File lib/active_support/cache/memcached_store.rb, line 128
def cas(name, options = nil)
  options = merged_options(options)
  key = normalize_key(name, options)

  handle_exceptions(return_value_on_error: false) do
    instrument(:cas, name, options) do
      @connection.cas(key, expiration(options)) do |raw_value|
        entry = deserialize_entry(raw_value)
        value = yield entry.value
        break true if read_only
        serialize_entry(Entry.new(value, **options), options)
      end
    end
    true
  end
end
cas_multi(*names, **options) { |values| ... } click to toggle source
# File lib/active_support/cache/memcached_store.rb, line 145
def cas_multi(*names, **options)
  return if names.empty?

  options = merged_options(options)
  keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]

  handle_exceptions(return_value_on_error: false) do
    instrument(:cas_multi, names, options) do
      @connection.cas(keys_to_names.keys, expiration(options)) do |raw_values|
        values = {}

        raw_values.each do |key, raw_value|
          entry = deserialize_entry(raw_value)
          values[keys_to_names[key]] = entry.value unless entry.expired?
        end

        values = yield values

        break true if read_only

        serialized_values = values.map do |name, value|
          [normalize_key(name, options), serialize_entry(Entry.new(value, **options), options)]
        end

        Hash[serialized_values]
      end
      true
    end
  end
end
clear(options = nil) click to toggle source
# File lib/active_support/cache/memcached_store.rb, line 194
def clear(options = nil)
  ActiveSupport::Notifications.instrument("cache_clear.active_support", options || {}) do
    @connection.flush
  end
end
delete(*) click to toggle source
Calls superclass method
# File lib/active_support/cache/memcached_store.rb, line 102
def delete(*)
  return true if read_only
  super
end
exist?(*) click to toggle source
Calls superclass method
# File lib/active_support/cache/memcached_store.rb, line 206
def exist?(*)
  !!super
end
read_multi(*names) click to toggle source
# File lib/active_support/cache/memcached_store.rb, line 107
def read_multi(*names)
  options = names.extract_options!
  return {} if names.empty?

  options = merged_options(options)
  keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]
  values = {}

  handle_exceptions(return_value_on_error: {}) do
    instrument(:read_multi, names, options) do
      if raw_values = @connection.get(keys_to_names.keys)
        raw_values.each do |key, value|
          entry = deserialize_entry(value)
          values[keys_to_names[key]] = entry.value unless entry.expired?
        end
      end
    end
    values
  end
end
stats() click to toggle source
# File lib/active_support/cache/memcached_store.rb, line 200
def stats
  ActiveSupport::Notifications.instrument("cache_stats.active_support") do
    @connection.stats
  end
end
write(*) click to toggle source
Calls superclass method
# File lib/active_support/cache/memcached_store.rb, line 97
def write(*)
  return true if read_only
  super
end

Private Instance Methods

deserialize_entry(value) click to toggle source
# File lib/active_support/cache/memcached_store.rb, line 319
def deserialize_entry(value)
  unless value.nil?
    value.is_a?(Entry) ? value : Entry.new(value, compress: false)
  end
end
expiration(options) click to toggle source
# File lib/active_support/cache/memcached_store.rb, line 333
def expiration(options)
  expires_in = options[:expires_in].to_i
  if expires_in > 0 && options[:race_condition_ttl] && !options[:raw]
    expires_in += options[:race_condition_ttl].to_i
  end
  expires_in
end
handle_exceptions(return_value_on_error:, on_miss: return_value_on_error, miss_exceptions: []) { || ... } click to toggle source
# File lib/active_support/cache/memcached_store.rb, line 341
def handle_exceptions(return_value_on_error:, on_miss: return_value_on_error, miss_exceptions: [])
  yield
rescue Memcached::NotFound, Memcached::ConnectionDataExists, *miss_exceptions
  on_miss
rescue Memcached::Error => e
  log_warning(e)
  raise unless @swallow_exceptions
  return_value_on_error
end
log_warning(err) click to toggle source
# File lib/active_support/cache/memcached_store.rb, line 351
def log_warning(err)
  return unless logger
  return if err.is_a?(Memcached::NotStored) && @swallow_exceptions

  logger.warn(
    "[MEMCACHED_ERROR] swallowed=#{@swallow_exceptions}" \
    " exception_class=#{err.class} exception_message=#{err.message}"
  )
end
normalize_key(key, options) click to toggle source
Calls superclass method
# File lib/active_support/cache/memcached_store.rb, line 310
def normalize_key(key, options)
  key = super.dup
  key = key.force_encoding(Encoding::ASCII_8BIT)
  key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" }
  # When we remove support to Rails 5.1 we can change the code to use ActiveSupport::Digest
  key = "#{key[0, 213]}:md5:#{::Digest::MD5.hexdigest(key)}" if key.size > 250
  key
end
read_serialized_entry(key, **) click to toggle source
# File lib/active_support/cache/memcached_store.rb, line 261
def read_serialized_entry(key, **)
  handle_exceptions(return_value_on_error: nil) do
    @connection.get(key)
  end
end
serialize_entry(entry, options) click to toggle source
# File lib/active_support/cache/memcached_store.rb, line 325
def serialize_entry(entry, options)
  if options[:raw]
    entry.value.to_s
  else
    entry
  end
end
write_serialized_entry(key, value, **options) click to toggle source
# File lib/active_support/cache/memcached_store.rb, line 273
def write_serialized_entry(key, value, **options)
  method = options && options[:unless_exist] ? :add : :set
  expires_in = expiration(options)
  handle_exceptions(return_value_on_error: false) do
    @connection.send(method, key, value, expires_in)
    true
  end
end