【问题标题】:Cache query on multiple request多个请求的缓存查询
【发布时间】:2020-06-30 07:53:48
【问题描述】:

我正在开发一个应用程序,其中我有一个用户模型,该模型具有 默认范围

default_scope { preload(:preference, :permission, :user_detail, :log) }

每次调用 current_user 都会触发 4 个查询

对于current_user,我已经覆盖了设计

module DeviseOverrides
  def self.included(base)
    base.class_eval {
      alias_method :devise_current_user, :current_user
      helper_method :current_user, :user_signed_in?
    }

    base.send(:include, InstanceMethods)
  end

  module InstanceMethods
    def current_user
      @current_user ||= warden.try(:user) || anonymous_user
    end
 
    def user_signed_in?
      warden.user.present?
    end

  end
end

假设我们在一个页面上有 5 个模块。并加载我们触发 ajax 调用的每个模块。基本上我们会触发 5 个请求。现在,对于每 5 个请求,@current_user 第一次将为零,它会触发一个查询。同样,当我们在同一个请求中调用 current_user 时,它将从缓存中返回。

我想要的是第一个请求应该触发查询并存储在@current_user 中。稍后对于页面中的所有其他请求,它应该从缓存中返回而不是触发查询

@current_user ||= warden.try(:user) || anonymous_user

warden.user.present?

谁能指导我需要做什么

class ApplicationController < ActionController::Base
  around_action :set_current_user

  def set_current_user
    User.current = current_user
    yield
  ensure
    User.current = nil
  end
end


class User < ApplicationRecord
  default_scope { preload(:preference, :permission, :user_detail, :log) 

  def self.current=(user)
    Thread.current[:current_user] = user
  end

  def self.current
    Thread.current[:current_user]
  end

end

根据@kgilpin 的建议,我更新了我的 application_controller 方法

def set_current_user
    User.current = warden.try(:user) || anonymous_user
    yield
  ensure
    User.current = nil
  end

【问题讨论】:

    标签: ruby-on-rails ruby devise ruby-on-rails-5.2


    【解决方案1】:

    您可以初始化当前用户并将其存储在线程局部变量中,然后从进程中的任何位置访问它。

    例如,您的ApplicationController 会执行以下操作:

      around_action :with_current_user
     
      protected
     
      def with_current_user
        Thread.current[:current_user] = warden.try(:user) || anonymous_user
        begin
          yield
        ensure
          Thread.current[:current_user] = nil
        end
      end
    end
    

    Thread.current[:current_user] 现在是 Ruby 进程同一线程/纤程中任意位置的当前用户。

    注意,Ruby 线程本地存储语法[], []= 也可以安全地与纤维一起使用。见https://docs.ruby-lang.org/en/2.6.0/Thread.html#method-i-5B-5D-3D

    【讨论】:

    • 我的应用程序控制器中已经有了相同的代码。我在我的问题中更新了同样的内容
    • 那么现在你的DeviseOverrides::InstanceMethods.current_user不应该返回User.current_user吗?
    • 我根据您建议的更改更新了我的方法。我还有一个疑问。我也有一个user_signed_in?在 devise_overrides 模块中,它再次触发 4 个查询。我该如何避免呢?
    【解决方案2】:

    使用 include 是不可能做到的,因为被包含模块的每个实例都有自己的 @current_user。

    您需要使用依赖注入或动态依赖解析机制在所有对象都可以访问它的范围内设置@current_user 变量。

    一个例子:

    class CurrentUserHolder
     include Singleton
    
     attr_accessor :current_user
    
    end
    
    module InstanceMethods
        def current_user
          CurrentUserHolder.instance.current_user ||= warden.try(:user) || anonymous_user
        end
    
    end
    

    【讨论】:

    • 现在如何调用 current_user 方法
    • 和以前一样。或者您可以拨打CurrentUserHolder.instance.current_user。请注意,由于没有事先设置,直接调用可能为 nil
    • 如何包含 InstanceMethods 模块?
    • 对整个应用程序来说是全局的当前用户似乎不对。
    • 这是 op 要求的;p。我会使用一些依赖注入机制,但不可能在系统上给定背景下给出一个 sn-p
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-07
    • 1970-01-01
    • 1970-01-01
    • 2011-10-09
    • 1970-01-01
    相关资源
    最近更新 更多