Rails.cache.исключение выборки: ошибка типа ( не может быть указано)

#ruby-on-rails #caching

#ruby-on-rails #кэширование

Вопрос:

Я реализую некоторое кэширование с помощью nifty Rails.cache.fetch . Однако в одном конкретном случае иногда я сталкиваюсь с исключением:

 TypeError in EmloController#index

Emlo can't be referred to

app/controllers/emlo_controller.rb:320:in `get_employees'
app/controllers/emlo_controller.rb:356:in `prepare_json_response'
app/controllers/emlo_controller.rb:23:in `block (2 levels) in index'
app/controllers/emlo_controller.rb:15:in `index'
  

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

Вот метод, который вызывает выборку кэша:

 def get_employees

  # This is for a AJAX refresh loop, so a 5-second cache actually helps quite a bit
  Rails.cache.fetch('emlo_all', :expires_in => 5.seconds, :race_condition_ttl => 1) do

    conditions = (params[:id]) ? {:user_id => params[:id]} : nil

    selections = [
      'employee_locations.id AS emlo_id',
      'employee_locations.status_id',
      'employee_locations.notes',
      'employee_locations.until',
      'employee_locations.updated_at',
      'employee_locations.user_id',
      'location_states.id AS state_id',
      'location_states.title AS status_string',
      'location_states.font_color',
      'location_states.bg_color',
      'users.displayname',
      'users.email',
      'users.mobile',
      'users.department',
      'users.extension',
      'users.guid',
      'users.dn'
    ].join(', ')

    Emlo.all(
        :select => selections,
        :joins => 'LEFT JOIN users ON employee_locations.user_id=users.id LEFT JOIN location_states ON employee_locations.status_id=location_states.id',
        :conditions => conditions,
        :order => 'users.displayname ASC'
    )
  end
end
  

Комментарии:

1. Ну, на данный момент я решил просто выполнить caches_action действие, которое в конечном итоге вызывает этот метод. Кажется, пока все работает нормально, но мне все еще интересно узнать, что кто-то еще может сказать об исключении, с которым я столкнулся.

2. Я только что столкнулся с этим снова, только на этот раз с Rails.cache.write() and .read() : » TypeError (<ModelName> can't be referred to) »

3. Похоже, вам нужно принудительно загрузить класс Emlo, иначе rails не знает, как десериализовать то, что находится в memcache.

Ответ №1:

Эта проблема возникает в режиме разработки, когда config.action_controller.perform_caching = true И config.cache_classes = false — кажется, что объекты ActiveRecord не могут быть сохранены Rails.cache .

Но если вам нужно включить config.action_controller.perform_caching в режиме разработки для тестирования кэширования, то вы также должны включить config.cache_classes . Однако это будет временным, потому что тогда вам придется перезапустить сервер разработки после изменения классов или файлов в конвейере ресурсов.

При отключенном кэшировании я бы использовал Rails.cache.write(some_name, some_value) if Rails.env.production? , чтобы предотвратить кеширование при разработке. Rails.cache.read() похоже, это не повлияло.

Комментарии:

1. LOL 10 лет спустя это все еще не исправлено!

Ответ №2:

Я столкнулся с чем-то подобным, хранящим массив экземпляров класса через Rails.cache.fetch(...) do — он будет работать нормально, пока я не изменю какой-либо код (что приведет к перезагрузке приложения), а затем выдаст «… не может быть передано».

Как упоминает @Matthew Clark выше, похоже, это связано с автоматической перезагрузкой класса, поскольку config.cache_classes в режиме разработки значение false равно false — это приводит к устареванию объектов, хранящихся в Rails.cache.fetch нем, поскольку они фактически относятся к классу, отличному от недавно перезагруженного.

В моем случае класс был тривиальным, поэтому я смог переместить его в lib каталог, чтобы он не перезагружался автоматически, и require это в моем коде. Если это было невозможно, возможно, вы могли бы сделать что-то вроде Rails.cache.fetch(... force: !Rails.configuration.cache_classes) , если бы вы не возражали против дополнительных накладных расходов в режиме разработки.

Ответ №3:

Сегодня вы можете написать wrokarround, следуя идее направляющих rails: https://guides.rubyonrails.org/classic_to_zeitwerk_howto.html#before-remove-const

Добавьте в initializer файл следующий код:

 if Rails.env.development?
  unless Rails.application.config.cache_classes
    Rails.autoloaders.main.on_unload do |klass, _abspath|
      Rails.cache.clear
    end
  end
end
  

Когда какой-либо класс необходимо перезагрузить, вы очищаете кеш, и он не завершается сбоем, единственным побочным эффектом будет на миллисекунды больше при каждом изменении кода, и при каждом изменении кеш также должен перезагружаться.

Ответ №4:

В зависимости от структуры вашего приложения при разработке может возникнуть ошибка, подобная этой: ошибка типа (пользователь не может быть передан) Эта ошибка вызвана некоторым безумием с перезагрузкой кэширования: промежуточное программное обеспечение, внедренное каким-либо драгоценным камнем, кэшируется. Но в процессе разработки ваши классы обычно таковыми не являются. Таким образом, некоторые классы могут быть недоступны при определенных обстоятельствах, например, если вы используете фильтры before для аутентификации пользователя, предоставляемые каким-либо движком. Вы должны быть в состоянии избавиться от указанной выше ошибки, включив кэширование классов. Попробуйте (и перезапустите сервер после этого):

development.rb

config.cache_classes = true

Если ошибка исчезла, вам повезло. Но поскольку кэширование классов в процессе разработки невозможно, снова отключите кэширование классов и явно требуйте класс, на который нельзя ссылаться. Т.Е.:

вершина development.rb

требуется «приложение / модели / пользователь»