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.

Другие вопросы по тегам