Rails设计:我如何(mem)缓存设备对用户对象的数据库请求?

时间:2011-04-25 18:20:47

标签: ruby-on-rails devise

每次访问经过身份验证的页面时,我都会注意到发布了一个SQL语句:

用户负载(0.2ms)SELECT users。* FROM users WHERE(usersid = 1)LIMIT 1

(我正在使用Rails 3 btw ..所以cache_money似乎是一个解决方案,尽管经过大量搜索我找不到替代品。)

我在用户模型中尝试了很多覆盖,似乎只调用了find_by_sql。哪个传递了整个SQL语句的字符串。像find_by_id或find这样直观的东西似乎没有被调用。我可以'覆盖这个方法并收集用户ID并从中做一个合理的缓存系统 - 但这很难看。

我也尝试覆盖authenticate_user,我可以拦截一次SQL尝试但是然后调用current_user似乎再试一次。

简单地说,我的用户对象很少变化,并且它是一个悲伤的状态,以保持为此而不是内存缓存解决方案。 (假设我愿意承担所有责任,使用以下内容来缓冲所述缓存:after_save作为部分但不是所有解决方案)

3 个答案:

答案 0 :(得分:13)

以下代码将按用户ID缓存用户 每次修改后都会使缓存失效。

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

答案 1 :(得分:1)

警告:最有可能采用更好/更智能的方式。

几个月前我把这个问题赶了出来。我发现 - 或者至少,我认为我发现 - 在这里Devise加载用户对象: https://github.com/plataformatec/devise/blob/master/lib/devise/rails/warden_compat.rb#L31

我在/initializers/warden.rb中为该反序列化方法创建了一个猴子补丁来进行缓存提取而不是获取。它感到肮脏和错误,但它确实有效。

答案 2 :(得分:1)

我也一直在努力解决这个问题。

这种不太复杂的方法是将此类方法添加到用户模型中:

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

请注意,我正在将模型名称添加到传入的对象ID中,以便从缓存中存储/检索对象;你可以使用适合你需要的任何方案。

当然,唯一需要担心的是当某些内容发生变化时,缓存中的用户无效。如果使用会话ID作为密钥的一部分将User存储在缓存中会很好,但会话在模型类中不可用,并且不会由Devise传递给此方法。