#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
требуется «приложение / модели / пользователь»