Как создать второй кеш памяти в хранилище Rails?

Я использую Rails 5. Я использую Rails-кеш-память для кэширования результатов запроса db, например, это в моей модели state.rb...

  def self.cached_find_by_iso_and_country_id(iso, country_id)
    if iso
      Rails.cache.fetch("#{iso.strip.upcase} #{country_id}") do
        find_by_iso_and_country_id(iso.strip.upcase, country_id)
      end
    end
  end

Мой вопрос в том, как я могу создать второй кеш-память Rails в памяти (мне нужно один для хранения файлов, которые я загрузил из Интернета), которые не мешают кешу запросов, который у меня выше? Я не хочу, чтобы записи в моем кэше файлов приводили к выходу записей в моем кеше запросов.

Ответ 1

Да, вы можете сделать это с помощью Rails. Вам нужно создать второй кеш и сделать его доступным в приложении как глобальную переменную, а затем вызвать соответствующий кеш в зависимости от контекста. Каждому кешу присваивается свой собственный блок памяти (по умолчанию 32 МБ), и если один кеш заполняется, это не повлияет на другой кеш. Это делается с помощью ActiveSupport::Cache::MemoryStore.new.

Я продемонстрирую, что два кэша не влияют друг на друга:

Сначала создайте два текстовых файла, которые будут использоваться для проверки кеша, один 10 МБ и один 30 МБ:

dd if=/dev/zero of=10M bs=1m count=10
dd if=/dev/zero of=30M bs=1m count=30

Откройте консоль Rails и прочитайте ее в строки:

ten    = File.read("10M"); 0
thirty = File.read("30M"); 0

Хранить ten в кеше:

Rails.cache.fetch("ten") { ten }; 0

Подтвердите, что данные были кешированы:

Rails.cache.fetch("ten")[0..10]
=> "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"

Хранить thirty в кеше:

Rails.cache.fetch("thirty") { thirty }; 0

Подтвердите, что это не было сохранено (слишком большое для сохранения в кеше при расширении в виде строки):

Rails.cache.fetch("thirty")[0..10]
NoMethodError: undefined method `[]' for nil:NilClass

Подтвердите, что вы исчерпали весь кеш:

Rails.cache.fetch("ten")[0..10]
NoMethodError: undefined method `[]' for nil:NilClass

Теперь создайте второй кеш и подтвердите, что он ведет себя идентично исходному кешу:

store = ActiveSupport::Cache::MemoryStore.new
store.fetch("ten") { ten }; 0
store.fetch("ten")[0..10]
=> "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
store.fetch("thirty") { thirty }; 0
store.fetch("thirty")[0..10]
NoMethodError: undefined method `[]' for nil:NilClass
store.fetch("ten")[0..10]
NoMethodError: undefined method `[]' for nil:NilClass

Теперь есть два пустых кеша: store и Rails.cache. Пусть подтверждают, что они независимы:

Rails.cache.fetch("ten") { ten }; 0
Rails.cache.fetch("ten")[0..10]
=> "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
store.fetch("thirty") { thirty }; 0  # bust the `store' cache
Rails.cache.fetch("ten")[0..10]
=> "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"

Если два кэша вмешались, то последний вызов store.fetch разобьет оба кэша. Это только busts store.

Чтобы реализовать второй кеш в своем приложении, создайте инициализатор config/initializers/cache.rb и добавьте:

$cache = ActiveSupport::Cache::MemoryStore.new

Вызовите новый кеш в свой код так же, как и Rails.cache:

$cache.fetch("foo") { "bar" }

Некоторые из этих деталей были взяты из этого ответа. Новый кеш поддерживает дополнительные параметры; проверьте MemoryStore и Кэширование с помощью Rails для более информацию о настройке кеша.

Это решение будет работать для небольших приложений. Обратите внимание на этот комментарий из MemoryStore docs:

Если вы запускаете несколько процессов сервера Ruby on Rails (что имеет место, если вы используете mongrel_cluster или Phusion Passenger), это означает, что экземпляры процесса сервера Rails не смогут совместно использовать данные кэша и это может быть не самый подходящий кэш в этом сценарии.