Rails Devise: Как мне (mem) кэшировать запросы базы данных устройства для объекта пользователя?
Каждый раз, когда я захожу на страницу, прошедшую проверку подлинности, я замечаю, что разработал инструкцию SQL:
Пользовательская нагрузка (0,2 мс) SELECT users
.* ОТ users
ГДЕ (users
,id
= 1) ПРЕДЕЛ 1
(Я использую Rails 3, кстати... так что cache_money кажется решением, и, несмотря на большой поиск, я не нашел замены).
Я перепробовал много переопределений в пользовательской модели, и кажется, что вызывается только find_by_sql. Который получает строку всего оператора SQL. Кажется, что-то интуитивно понятное, например find_by_id или find, не вызывается. Я "могу" переопределить этот метод и получить идентификатор пользователя и сделать разумную систему кеша из этого - но это довольно уродливо.
Я также попытался переопределить authenticate_user, который я могу перехватить одну попытку SQL, но затем вызовы current_user, кажется, пытаются снова.
Проще говоря, мои пользовательские объекты меняются редко, и это печальное состояние - продолжать использовать для этого базу данных вместо решения memcache. (предположим, что я готов принять на себя всю ответственность за аннулирование указанного кэша с помощью: after_save как части, но не всего этого решения)
3 ответа
Следующий код кеширует пользователя по его идентификатору и делает кеш недействительным после каждой модификации.
class User < ActiveRecord::Base
after_save :invalidate_cache
def self.serialize_from_session(key, salt)
single_key = key.is_a?(Array) ? key.first : key
user = Rails.cache.fetch("user:#{single_key}") do
User.where(:id => single_key).entries.first
end
# validate user against stored salt in the session
return user if user && user.authenticatable_salt == salt
# fallback to devise default method if user is blank or invalid
super
end
private
def invalidate_cache
Rails.cache.delete("user:#{id}")
end
end
ВНИМАНИЕ: Скорее всего, есть лучший / умный способ сделать это.
Я преследовал эту проблему несколько месяцев назад. Я нашел - или, по крайней мере, я думаю, что нашел - где Devise загружает пользовательский объект здесь: https://github.com/plataformatec/devise/blob/master/lib/devise/rails/warden_compat.rb#L31
Я создал патч обезьяны для этого десериализованного метода в /initializers/warden.rb, чтобы сделать выборку из кэша вместо get. Это было грязно и неправильно, но это сработало.
Я тоже боролся с этим.
Менее запутанный способ сделать это - добавить метод класса в вашу модель User:
def self.serialize_from_session(key, salt)
single_key = key.is_a?(Array) ? key.first : key
Rails.cache.fetch("user:#{single_key}") { User.find(single_key) }
end
Обратите внимание, что я добавляю имя модели к идентификатору объекта, который передается для хранения / извлечения объекта из кэша; Вы можете использовать любую схему, которая соответствует вашим потребностям.
Конечно, единственное, о чем нужно беспокоиться, это сделать пользователя недействительным в кеше, когда что-то меняется. Вместо этого было бы неплохо сохранить пользователя в кеше, используя идентификатор сеанса как часть ключа, но сеанс недоступен в классе модели и не передается в этот метод Devise.