класс ActiveSupport::Cache::Store
Хранилище кэша Active Support
Абстрактный класс хранилища кэша. Существует несколько реализаций хранилищ кэша, каждая из которых имеет собственные дополнительные возможности. См. классы в модуле ActiveSupport::Cache, например ActiveSupport::Cache::MemCacheStore. В настоящее время MemCacheStore — самое популярное хранилище кэша для крупных рабочих веб-сайтов.
Некоторые реализации могут не поддерживать все методы, кроме основных методов работы с кэшем: fetch, write, read, exist? и delete.
ActiveSupport::Cache::Store может хранить любые объекты Ruby, поддерживаемые методами dump и load класса coder.
cache = ActiveSupport::Cache::MemoryStore.new
cache.read('city') # => nil
cache.write('city', "Duckburgh") # => true
cache.read('city') # => "Duckburgh"
cache.write('not serializable', Proc.new {}) # => TypeError
Ключи всегда преобразуются в строки, и регистр символов в них учитывается. Если в качестве ключа указан объект, у которого определён метод cache_key, этот метод будет вызван для формирования ключа. В противном случае будет вызван метод to_param. В качестве ключей также можно использовать хэши и массивы. Элементы будут разделены косыми чертами, а элементы внутри Hash будут отсортированы по ключу, чтобы обеспечить единообразие.
cache.read('city') == cache.read(:city) # => true
Значения Nil можно кэшировать.
Если ваш кэш использует общую инфраструктуру, можно задать пространство имён для записей кэша. Если пространство имён задано, оно будет добавляться перед каждым ключом. Пространство имён может быть статическим значением или Proc. Если это Proc, он будет вызываться при вычислении каждого ключа, что позволит использовать логику приложения для аннулирования ключей.
cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
@last_mod_time = Time.now # Invalidate the entire cache by changing namespace
Константы
- DEFAULT_POOL_OPTIONS
-
Параметры
ConnectionPoolпо умолчанию - MAX_KEY_SIZE
-
Если длина ключа превышает допустимый предел, он усекается с помощью дайджеста Active Support.
Атрибуты
Публичные методы класса
# File activesupport/lib/active_support/cache.rb, line 300
def initialize(options = nil)
@options = options ? validate_options(normalize_options(options)) : {}
@options[:compress] = true unless @options.key?(:compress)
@options[:compress_threshold] ||= DEFAULT_COMPRESS_LIMIT
@max_key_size = @options.delete(:max_key_size)
@max_key_size = MAX_KEY_SIZE if @max_key_size.nil? # allow 'false' as a value
@coder = @options.delete(:coder) do
legacy_serializer = Cache.format_version < 7.1 && !@options[:serializer]
serializer = @options.delete(:serializer) || default_serializer
serializer = Cache::SerializerWithFallback[serializer] if serializer.is_a?(Symbol)
compressor = @options.delete(:compressor) { Zlib }
Cache::Coder.new(serializer, compressor, legacy_serializer: legacy_serializer)
end
@coder ||= Cache::SerializerWithFallback[:passthrough]
@coder_supports_compression = @coder.respond_to?(:dump_compressed)
end Создаёт новое хранилище кэша.
Параметры
:namespace-
Задаёт пространство имён для кэша. Этот параметр особенно полезен, если приложение использует кэш совместно с другими приложениями.
:serializer-
Сериализатор кэшируемых значений. Должен отвечать на методы
dumpиload.Сериализатор по умолчанию зависит от версии формата кэша (задаётся с помощью
config.active_support.cache_format_versionпри использованииRails). Сериализатор по умолчанию для каждой версии формата включает механизм резервной десериализации значений из любой версии формата. Это упрощает переход между версиями формата без аннулирования всего кэша.Также можно указать
serializer: :message_pack, чтобы использовать предварительно настроенный сериализатор на основеActiveSupport::MessagePack. Сериализатор:message_packвключает тот же механизм резервной десериализации, упрощая переход с сериализатора по умолчанию или на него. Сериализатор:message_packможет повысить производительность, но для него требуется гемmsgpack. :compressor-
Компрессор сериализованных значений кэша. Должен отвечать на методы
deflateиinflate.По умолчанию используется компрессор
Zlib. Чтобы определить новый пользовательский компрессор, который также распаковывает старые записи кэша, можно проверять сжатые значения на наличие сигнатуры"\x78"из Zlib:module MyCompressor def self.deflate(dumped) # compression logic... (make sure result does not start with "\x78"!) end def self.inflate(compressed) if compressed.start_with?("\x78") Zlib.inflate(compressed) else # decompression logic... end end end ActiveSupport::Cache.lookup_store(:redis_cache_store, compressor: MyCompressor) :coder-
Кодировщик для сериализации и (при необходимости) сжатия записей кэша. Должен отвечать на методы
dumpиload.Кодировщик по умолчанию объединяет сериализатор и компрессор и включает несколько оптимизаций производительности. Если нужно переопределить только сериализатор или компрессор, следует указать параметры
:serializerили:compressorсоответственно.Если хранилище может обрабатывать записи кэша напрямую, можно также указать
coder: nil, чтобы не использовать сериализатор, компрессор и кодировщик. Например, если используетсяActiveSupport::Cache::MemoryStoreи можно гарантировать, что значения кэша не будут изменяться, укажитеcoder: nil, чтобы избежать дополнительных затрат на защиту от изменений.Параметр
:coderнельзя использовать вместе с параметрами:serializerи:compressor. Их совместное указание вызовет исключениеArgumentError.
Все остальные указанные параметры рассматриваются как параметры по умолчанию для соответствующих операций с кэшем, таких как read, write и fetch.
Публичные методы экземпляра
# File activesupport/lib/active_support/cache.rb, line 785
def cleanup(options = nil)
raise NotImplementedError.new("#{self.class.name} does not support cleanup")
end Очищает кэш, удаляя просроченные записи.
Параметры передаются базовой реализации кэша.
Некоторые реализации могут не поддерживать этот метод.
# File activesupport/lib/active_support/cache.rb, line 795
def clear(options = nil)
raise NotImplementedError.new("#{self.class.name} does not support clear")
end Полностью очищает кэш. Используйте этот метод с осторожностью: если кэш общий, это может повлиять на другие процессы.
Хэш параметров передаётся базовой реализации кэша.
Некоторые реализации могут не поддерживать этот метод.
# File activesupport/lib/active_support/cache.rb, line 750
def decrement(name, amount = 1, options = nil)
raise NotImplementedError.new("#{self.class.name} does not support decrement")
end Уменьшает целочисленное значение в кэше.
Параметры передаются базовой реализации кэша.
Некоторые реализации могут не поддерживать этот метод.
# File activesupport/lib/active_support/cache.rb, line 686
def delete(name, options = nil)
options = merged_options(options)
key = normalize_key(name, options)
instrument(:delete, key, options) do
delete_entry(key, **options)
end
end Удаляет запись из кэша. Возвращает true, если запись удалена, и false в противном случае.
Параметры передаются базовой реализации кэша.
# File activesupport/lib/active_support/cache.rb, line 732
def delete_matched(matcher, options = nil)
raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
end Удаляет все записи с ключами, соответствующими шаблону.
Параметры передаются базовой реализации кэша.
Некоторые реализации могут не поддерживать этот метод.
# File activesupport/lib/active_support/cache.rb, line 699
def delete_multi(names, options = nil)
return 0 if names.empty?
options = merged_options(options)
names.map! { |key| normalize_key(key, options) }
instrument_multi(:delete_multi, names, options) do
delete_multi_entries(names, **options)
end
end Удаляет несколько записей из кэша. Возвращает количество удалённых записей.
Параметры передаются базовой реализации кэша.
# File activesupport/lib/active_support/cache.rb, line 713
def exist?(name, options = nil)
options = merged_options(options)
key = normalize_key(name, options)
instrument(:exist?, key) do |payload|
entry = read_entry(key, **options, event: payload)
(entry && !entry.expired? && !entry.mismatched?(normalize_version(name, options))) || false
end
end Возвращает true, если в кэше есть запись с указанным ключом.
Параметры передаются базовой реализации кэша.
# File activesupport/lib/active_support/cache.rb, line 452
def fetch(name, options = nil, &block)
if block_given?
options = merged_options(options)
key = normalize_key(name, options)
entry = nil
unless options[:force]
instrument(:read, key, options) do |payload|
cached_entry = read_entry(key, **options, event: payload)
entry = handle_expired_entry(cached_entry, key, options)
if entry
if entry.mismatched?(normalize_version(name, options))
entry = nil
else
begin
entry.value
rescue DeserializationError
entry = nil
end
end
end
payload[:super_operation] = :fetch if payload
payload[:hit] = !!entry if payload
end
end
if entry
get_entry_value(entry, name, options)
else
save_block_result_to_cache(name, key, options, &block)
end
elsif options && options[:force]
raise ArgumentError, "Missing block: Calling `Cache#fetch` with `force: true` requires a block."
else
read(name, options)
end
end Извлекает данные из кэша по указанному ключу. Если в кэше есть данные с таким ключом, они возвращаются.
Если таких данных в кэше нет (промах кэша), будет возвращено nil. Однако если передан блок, ему будет передан ключ, и блок будет выполнен при промахе кэша. Возвращаемое блоком значение будет записано в кэш под указанным ключом и возвращено.
cache.write('today', 'Monday')
cache.fetch('today') # => "Monday"
cache.fetch('city') # => nil
cache.fetch('city') do
'Duckburgh'
end
cache.fetch('city') # => "Duckburgh"
Параметры
Внутри fetch вызывает read_entry, а при промахе кэша — write_entry. Поэтому fetch поддерживает те же параметры, что и read и write. Кроме того, fetch поддерживает следующие параметры:
-
force: true— принудительно вызывает промах кэша: значение кэша считается отсутствующим, даже если оно есть. Еслиforceимеет значение true, необходимо передать блок, чтобы в результате всегда выполнялась запись в кэш.cache.write('today', 'Monday') cache.fetch('today', force: true) { 'Tuesday' } # => 'Tuesday' cache.fetch('today', force: true) # => ArgumentErrorПараметр
:forceполезен, когда вы вызываете другой метод, чтобы определить, следует ли принудительно записать значение в кэш. В остальных случаях понятнее просто вызватьwrite. -
skip_nil: true— предотвращает кэширование результата nil:cache.fetch('foo') { nil } cache.fetch('bar', skip_nil: true) { nil } cache.exist?('foo') # => true cache.exist?('bar') # => false -
:race_condition_ttl— задаёт количество секунд, в течение которых просроченное значение можно повторно использовать, пока создаётся новое. Это позволяет избежать состязаний при истечении срока действия записей кэша и не допустить одновременной повторной генерации одной и той же записи несколькими процессами (также известной как эффект лавины запросов).Когда процесс обнаруживает запись кэша, срок действия которой истёк менее
:race_condition_ttlсекунд назад, он продлевает срок её действия на:race_condition_ttlсекунд перед созданием нового значения. В течение этого дополнительного промежутка времени другие процессы продолжают использовать старое значение, пока первый процесс создаёт новое. После того как первый процесс запишет новое значение, остальные процессы начнут использовать его.Если при создании нового значения в первом процессе произойдёт ошибка, другой процесс сможет попытаться создать новое значение после истечения дополнительного промежутка времени.
# Set all values to expire after one second. cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1) cache.write("foo", "original value") val_1 = nil val_2 = nil p cache.read("foo") # => "original value" sleep 1 # wait until the cache expires t1 = Thread.new do # fetch does the following: # 1. gets an recent expired entry # 2. extends the expiry by 2 seconds (race_condition_ttl) # 3. regenerates the new value val_1 = cache.fetch("foo", race_condition_ttl: 2) do sleep 1 "new value 1" end end # Wait until t1 extends the expiry of the entry # but before generating the new value sleep 0.1 val_2 = cache.fetch("foo", race_condition_ttl: 2) do # This block won't be executed because t1 extended the expiry "new value 2" end t1.join p val_1 # => "new value 1" p val_2 # => "original value" p cache.fetch("foo") # => "new value 1" # The entry requires 3 seconds to expire (expires_in + race_condition_ttl) # We have waited 2 seconds already (sleep(1) + t1.join) thus we need to wait 1 # more second to see the entry expire. sleep 1 p cache.fetch("foo") # => nil
Динамические параметры
В некоторых случаях может потребоваться динамически вычислять параметры на основе кэшированного значения. Для этого экземпляр ActiveSupport::Cache::WriteOptions передаётся блоку в качестве второго аргумента. Например:
cache.fetch("authentication-token:#{user.id}") do |key, options|
token = authenticate_to_service
options.expires_at = token.expires_at
token
end
# File activesupport/lib/active_support/cache.rb, line 603
def fetch_multi(*names)
raise ArgumentError, "Missing block: `Cache#fetch_multi` requires a block." unless block_given?
return {} if names.empty?
options = names.extract_options!
options = merged_options(options)
keys = names.map { |name| normalize_key(name, options) }
writes = {}
ordered = instrument_multi :read_multi, keys, options do |payload|
if options[:force]
reads = {}
else
reads = read_multi_entries(names, **options)
end
ordered = names.index_with do |name|
reads.fetch(name) { writes[name] = yield(name) }
end
writes.compact! if options[:skip_nil]
payload[:hits] = reads.keys.map { |name| normalize_key(name, options) }
payload[:super_operation] = :fetch_multi
ordered
end
write_multi(writes, options)
ordered
end Извлекает данные из кэша по указанным ключам. Если в кэше есть данные с указанными ключами, они возвращаются. В противном случае для каждого ключа, для которого данные отсутствуют, вызывается переданный блок, а его результат записывается в кэш и возвращается. Поэтому необходимо передать блок, возвращающий данные для записи в кэш. Если не нужно записывать данные в кэш при отсутствии значения, используйте read_multi.
Возвращает хэш с данными для каждого имени. Например:
cache.write("bim", "bam")
cache.fetch_multi("bim", "unknown_key") do |key|
"Fallback value for key: #{key}"
end
# => { "bim" => "bam",
# "unknown_key" => "Fallback value for key: unknown_key" }
Дополнительные параметры также можно указать с помощью аргумента options. Подробности см. в описании fetch. Остальные параметры передаются базовой реализации кэша. Например:
cache.fetch_multi("fizz", expires_in: 5.seconds) do |key|
"buzz"
end
# => {"fizz"=>"buzz"}
cache.read("fizz")
# => "buzz"
sleep(6)
cache.read("fizz")
# => nil
# File activesupport/lib/active_support/cache.rb, line 741
def increment(name, amount = 1, options = nil)
raise NotImplementedError.new("#{self.class.name} does not support increment")
end Увеличивает целочисленное значение в кэше.
Параметры передаются базовой реализации кэша.
Некоторые реализации могут не поддерживать этот метод.
# File activesupport/lib/active_support/cache.rb, line 330 def mute previous_silence, @silence = @silence, true yield ensure @silence = previous_silence end
Отключает журналирование внутри блока.
# File activesupport/lib/active_support/cache.rb, line 800 def namespace @options[:namespace] end
Возвращает текущее пространство имён
# File activesupport/lib/active_support/cache.rb, line 806 def namespace=(namespace) @options[:namespace] = namespace end
Задаёт текущее пространство имён. Обратите внимание: это значение будет проигнорировано, если при вызовах методов кэша переданы пользовательские параметры с ключом пространства имён.
# File activesupport/lib/active_support/cache.rb, line 506
def read(name, options = nil)
options = merged_options(options)
key = normalize_key(name, options)
version = normalize_version(name, options)
instrument(:read, key, options) do |payload|
entry = read_entry(key, **options, event: payload)
if entry
if entry.expired?
delete_entry(key, **options)
payload[:hit] = false if payload
nil
elsif entry.mismatched?(version)
payload[:hit] = false if payload
nil
else
payload[:hit] = true if payload
begin
entry.value
rescue DeserializationError
payload[:hit] = false
nil
end
end
else
payload[:hit] = false if payload
nil
end
end
end Читает данные из кэша по указанному ключу. Если в кэше есть данные с таким ключом, они возвращаются. В противном случае возвращается nil.
Обратите внимание: если данные были записаны с параметрами :expires_in или :version, оба этих условия проверяются до возврата данных.
Параметры
-
:namespace— заменяет пространство имён хранилища для этого вызова. -
:version— задаёт версию записи кэша. Если версия в кэше не совпадает с запрошенной, чтение считается промахом кэша. Эта возможность используется для поддержки повторно используемых ключей кэша.
Обработка остальных параметров зависит от конкретной реализации хранилища кэша.
# File activesupport/lib/active_support/cache.rb, line 762 def read_counter(name, **options) options = merged_options(options).merge(raw: true) read(name, **options)&.to_i end
Читает счётчик, заданный с помощью increment / decrement.
cache.write_counter("foo", 1)
cache.read_counter("foo") # => 1
cache.increment("foo")
cache.read_counter("foo") # => 2
Параметры передаются базовой реализации кэша.
# File activesupport/lib/active_support/cache.rb, line 544
def read_multi(*names)
return {} if names.empty?
options = names.extract_options!
options = merged_options(options)
keys = names.map { |name| normalize_key(name, options) }
instrument_multi :read_multi, keys, options do |payload|
read_multi_entries(names, **options, event: payload).tap do |results|
payload[:hits] = results.keys.map { |name| normalize_key(name, options) }
end
end
end Одновременно читает из кэша несколько значений. Параметры можно передать последним аргументом.
Некоторые реализации кэша могут оптимизировать этот метод.
Возвращает хэш, сопоставляющий переданные имена найденным значениям.
# File activesupport/lib/active_support/cache.rb, line 324 def silence! @silence = true self end
Отключает журналирование.
# File activesupport/lib/active_support/cache.rb, line 672
def write(name, value, options = nil)
options = merged_options(options)
key = normalize_key(name, options)
instrument(:write, key, options) do
entry = Entry.new(value, **options, version: normalize_version(name, options))
write_entry(key, entry, **options)
end
end Записывает значение в кэш по указанному ключу. Значение должно поддерживаться методами dump и load класса coder.
Возвращает true, если запись выполнена успешно, nil, если при обращении к серверной части кэша произошла ошибка, или false, если запись не удалась по другой причине.
По умолчанию записи кэша размером более 1 кБ сжимаются. Сжатие позволяет хранить больше данных в том же объёме памяти, сокращая количество вытеснений записей из кэша и повышая частоту попаданий в кэш.
Параметры
-
compress: false— отключает сжатие записи кэша. -
:compress_threshold— порог сжатия в байтах. Записи кэша, размер которых превышает этот порог, будут сжаты. По умолчанию используется значение1.kilobyte. -
:expires_in— задаёт относительный срок действия записи кэша в секундах.:expire_inи:expired_in— псевдонимы для:expires_in.cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes) cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
-
:expires_at— задаёт абсолютное время истечения срока действия записи кэша.cache = ActiveSupport::Cache::MemoryStore.new cache.write(key, value, expires_at: Time.now.at_end_of_hour)
-
:version— задаёт версию записи кэша. При чтении из кэша, если версия в кэше не совпадает с запрошенной, чтение считается промахом кэша. Эта возможность используется для поддержки повторно используемых ключей кэша. -
:unless_exist— предотвращает перезапись существующей записи кэша.
Обработка остальных параметров зависит от конкретной реализации хранилища кэша.
# File activesupport/lib/active_support/cache.rb, line 775 def write_counter(name, value, **options) options = merged_options(options).merge(raw: true) write(name, value.to_i, **options) end
Записывает счётчик, который затем можно изменять с помощью increment / decrement.
cache.write_counter("foo", 1)
cache.read_counter("foo") # => 1
cache.increment("foo")
cache.read_counter("foo") # => 2
Параметры передаются базовой реализации кэша.
# File activesupport/lib/active_support/cache.rb, line 559
def write_multi(hash, options = nil)
return hash if hash.empty?
options = merged_options(options)
normalized_hash = hash.transform_keys { |key| normalize_key(key, options) }
instrument_multi :write_multi, normalized_hash, options do |payload|
entries = hash.each_with_object({}) do |(name, value), memo|
memo[normalize_key(name, options)] = Entry.new(value, **options, version: normalize_version(name, options))
end
write_multi_entries entries, **options
end
end API хранилища Cache для одновременной записи нескольких значений.
Закрытые методы экземпляра
# File activesupport/lib/active_support/cache.rb, line 826
def key_matcher(pattern, options) # :doc:
prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
if prefix
source = pattern.source
if source.start_with?("^")
source = source[1, source.length]
else
source = ".*#{source[0, source.length]}"
end
Regexp.new("^#{Regexp.escape(prefix)}:#{source}", pattern.options)
else
pattern
end
end Добавляет пространство имён, заданное в параметрах, к шаблону для сопоставления ключей. Реализации, поддерживающие delete_matched, должны вызывать этот метод, чтобы преобразовать шаблон, соответствующий именам, в шаблон, соответствующий ключам с пространством имён.
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.