class ActiveSupport::Cache::MemCacheStore
A cache store implementation which stores data in Memcached: memcached.org
This is currently the most popular cache store for production websites.
Special features:
-
Clustering and load balancing. One can specify multiple memcached servers, and
MemCacheStore
will load balance between all available servers. If a server goes down, thenMemCacheStore
will ignore it until it comes back up.
MemCacheStore
implements the Strategy::LocalCache
strategy which implements an in-memory cache inside of a block.
Constants
- ESCAPE_KEY_CHARS
Public Class Methods
Creates a new MemCacheStore
object, with the given memcached server addresses. Each address is either a host name, or a host-with-port string in the form of “host_name:port”. For example:
ActiveSupport::Cache::MemCacheStore.new("localhost", "server-downstairs.localnetwork:8229")
If no addresses are provided, but ENV['MEMCACHE_SERVERS']
is defined, it will be used instead. Otherwise, MemCacheStore
will connect to localhost:11211 (the default memcached port).
ActiveSupport::Cache::Store::new
# File lib/active_support/cache/mem_cache_store.rb, line 109 def initialize(*addresses) addresses = addresses.flatten options = addresses.extract_options! if options.key?(:cache_nils) options[:skip_nil] = !options.delete(:cache_nils) end super(options) unless [String, Dalli::Client, NilClass].include?(addresses.first.class) raise ArgumentError, "First argument must be an empty array, an array of hosts or a Dalli::Client instance." end if addresses.first.is_a?(Dalli::Client) @data = addresses.first else mem_cache_options = options.dup # The value "compress: false" prevents duplicate compression within Dalli. mem_cache_options[:compress] = false (UNIVERSAL_OPTIONS - %i(compress)).each { |name| mem_cache_options.delete(name) } @data = self.class.build_mem_cache(*(addresses + [mem_cache_options])) end end
Advertise cache versioning support.
# File lib/active_support/cache/mem_cache_store.rb, line 30 def self.supports_cache_versioning? true end
Public Instance Methods
Clear the entire cache on all memcached servers. This method should be used with care when shared cache is being used.
# File lib/active_support/cache/mem_cache_store.rb, line 175 def clear(options = nil) rescue_error_with(nil) { @data.with { |c| c.flush_all } } end
Decrement a cached value. This method uses the memcached decr 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 to zero.
# File lib/active_support/cache/mem_cache_store.rb, line 164 def decrement(name, amount = 1, options = nil) options = merged_options(options) instrument(:decrement, name, amount: amount) do rescue_error_with nil do @data.with { |c| c.decr(normalize_key(name, options), amount, options[:expires_in]) } end end 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 to zero.
# File lib/active_support/cache/mem_cache_store.rb, line 151 def increment(name, amount = 1, options = nil) options = merged_options(options) instrument(:increment, name, amount: amount) do rescue_error_with nil do @data.with { |c| c.incr(normalize_key(name, options), amount, options[:expires_in]) } end end end
Get the statistics from the memcached servers.
# File lib/active_support/cache/mem_cache_store.rb, line 180 def stats @data.with { |c| c.stats } end
Behaves the same as ActiveSupport::Cache::Store#write
, but supports additional options specific to memcached.
Additional Options¶ ↑
-
raw: true
- Sends the value directly to the server as raw bytes. The value must be a string or number. You can use memcached direct operations likeincrement
anddecrement
only on raw values. -
unless_exist: true
- Prevents overwriting an existing cache entry.
# File lib/active_support/cache/mem_cache_store.rb, line 132
Private Instance Methods
# File lib/active_support/cache/mem_cache_store.rb, line 229 def default_coder Coders[Cache.format_version] end
Delete an entry from the cache.
# File lib/active_support/cache/mem_cache_store.rb, line 282 def delete_entry(key, **options) rescue_error_with(false) { @data.with { |c| c.delete(key) } } end
ActiveSupport::Cache::Store#deserialize_entry
# File lib/active_support/cache/mem_cache_store.rb, line 307 def deserialize_entry(payload, raw: false, **) if payload && raw Entry.new(payload) else super(payload) end end
Memcache keys are binaries. So we need to force their encoding to binary before applying the regular expression to ensure we are escaping all characters properly.
ActiveSupport::Cache::Store#normalize_key
# File lib/active_support/cache/mem_cache_store.rb, line 297 def normalize_key(key, options) key = super if key key = key.dup.force_encoding(Encoding::ASCII_8BIT) key = key.gsub(ESCAPE_KEY_CHARS) { |match| "%#{match.getbyte(0).to_s(16).upcase}" } key = "#{key[0, 212]}:hash:#{ActiveSupport::Digest.hexdigest(key)}" if key.size > 250 end key end
Read an entry from the cache.
# File lib/active_support/cache/mem_cache_store.rb, line 234 def read_entry(key, **options) deserialize_entry(read_serialized_entry(key, **options), **options) end
Reads multiple entries from the cache implementation.
# File lib/active_support/cache/mem_cache_store.rb, line 264 def read_multi_entries(names, **options) keys_to_names = names.index_by { |name| normalize_key(name, options) } raw_values = @data.with { |c| c.get_multi(keys_to_names.keys) } values = {} raw_values.each do |key, value| entry = deserialize_entry(value, raw: options[:raw]) unless entry.expired? || entry.mismatched?(normalize_version(keys_to_names[key], options)) values[keys_to_names[key]] = entry.value end end values end
# File lib/active_support/cache/mem_cache_store.rb, line 238 def read_serialized_entry(key, **options) rescue_error_with(nil) do @data.with { |c| c.get(key, options) } end end
# File lib/active_support/cache/mem_cache_store.rb, line 315 def rescue_error_with(fallback) yield rescue Dalli::DalliError => error ActiveSupport.error_reporter&.report(error, handled: true, severity: :warning) logger.error("DalliError (#{error}): #{error.message}") if logger fallback end
ActiveSupport::Cache::Store#serialize_entry
# File lib/active_support/cache/mem_cache_store.rb, line 286 def serialize_entry(entry, raw: false, **options) if raw entry.value.to_s else super(entry, raw: raw, **options) end end
Write an entry to the cache.
# File lib/active_support/cache/mem_cache_store.rb, line 245 def write_entry(key, entry, **options) write_serialized_entry(key, serialize_entry(entry, **options), **options) end
# File lib/active_support/cache/mem_cache_store.rb, line 249 def write_serialized_entry(key, payload, **options) method = options[:unless_exist] ? :add : :set expires_in = options[:expires_in].to_i if options[:race_condition_ttl] && expires_in > 0 && !options[:raw] # Set the memcache expire a few minutes in the future to support race condition ttls on read expires_in += 5.minutes end rescue_error_with false do # Don't pass compress option to Dalli since we are already dealing with compression. options.delete(:compress) @data.with { |c| c.send(method, key, payload, expires_in, **options) } end end