2

いくつかのSQL呼び出しを熱心にロードして、アプリを高速化しようとしています。管理者の承認を処理するために CanCan gem を使用しています。3 つの異なるロールを持つ Roles テーブルと、多対多のテーブル roles_users があります。ページが CanCan 機能セットで読み込まれるたびに、3 つの個別の SQL クエリが実行されます。

 Role Load (0.6ms)  SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles"."id"    
= "roles_users"."role_id" WHERE "roles_users"."user_id" = 2 AND "roles"."name" = 'admin'  
  LIMIT 1
Role Load (0.4ms)  SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles"."id" 
  = "roles_users"."role_id" WHERE "roles_users"."user_id" = 2 AND "roles"."name" =  
 'manager' LIMIT 1
 Role Load (0.3ms)  SELECT "roles".* FROM "roles" INNER JOIN "roles_users" ON "roles"."id" 
  = "roles_users"."role_id" WHERE "roles_users"."user_id" = 2 AND "roles"."name" = 'user' 
  LIMIT 1 

私は default_scope :include => :roles を User クラスに入れ、 has_and_belongs_to_many 呼び出しに :includes を入れてみました。

1 つの SQL クエリのみを使用するように、Roles テーブルをどこでイーガー ロードできますか?

4

2 に答える 2

2

以下のようなコードがあるようです。つまり、ロール レコード内の値と比較する必要がある文字列または記号が必要であり、ロール自体ではありません。したがって、次のようなものを探します。

def do_i_have_role(role_name_to_check)
  self.roles.detect do |role|
    role.name == role_name_to_check
  end
end

ここで熱心な読み込みを行って修正することもできますが、この猫の皮を剥ぐ別の方法は、クエリを再構築することです。つまり、最初に役割オブジェクトを探してから、アカウントにその役割があるかどうかを確認します。

def do_i_have_role(role_name_to_check)
  role = Role.where(:name => role_name_to_check)
  self.roles.include? role
end

これで、ロール データベースへのヒットは 3 回ではなく 1 回になりました (さらに、accounts テーブルへのヒットと、account_user テーブルへのヒットはおそらく回避できません)。

于 2012-04-25T15:41:04.607 に答える
1

cancan システムのクエリに使用しているコード (cancan への API 呼び出し) を表示することで、質問を改善できます。

ユーザーが管理者、マネージャー、またはユーザーのカテゴリに属しているかどうかを確認するために、個別のクエリを実行しているようです。

最も可能性の高いユーザー タイプが最初にクエリされるように、クエリを並べ替える必要があります。たとえば、ある人がユーザーである可能性が最も高い場合は、最初にそれを照会します。

さらに良いことに、セッションでユーザーのロールをキャッシュします。そうすれば、クエリを 1 回だけ実行できます。

追加した

ログイン/ログアウト後のフックをdeviseで使用して、セッションに値を設定できます。アクション コントローラー フィルターを使用して、(セッションから) ロールを検索できます。セッション ドキュメント

セキュリティ上の問題

  • セッション内の役割をクリアするには、devise after logout フックを使用します。
  • テストがセッションのロール ID をカバーしていることを確認してください
  • 人々が自分の役割を自己アップグレードすることを心配する必要はありません。セッションは、変更に対してデジタル署名されています。セッションのドキュメントを参照してください。
  • その場で削除/変更されるユーザー:

セキュリティのレベルによっては、ユーザーが削除または格下げされた状況をカバーする必要があり、(ユーザーが次にログインするまで待機するのではなく) すぐに変更を適用したい場合があります。

そうするのは難しいかもしれません。いくつかのテクニック:

  • 1 つの方法は、role_id と一緒にセッションに role-checked-timestamp を保存することです。次に、30 分以上経過している場合は、role_id を無効にします (もう一度調べます)。
  • または、ユーザーの role_id をセッション ストアではなく memcache に保存します。次に、(delete-user を実行した) 他のプロセスは、アクティブなユーザー セッションのキャッシュから role_id 値を削除できます。
  • より単純な強引な方法: 管理者に現在のすべてのセッションをクリアする方法を提供します (現在のすべてのユーザーが再度ログインする必要があります)。これにより、システムから誰かを起動しなければならないというまれな状況に対処できます。
  • 多くのシステムと同様に、数時間後にすべてのセッションをタイムアウトにします。または、1 日 1 回、すべてのセッションをクリアします。これを行う場合は、自動再ログインを実装してテストするとよいでしょう。再ログイン後に要求されたページに戻ることを含みます。再ログインすると、資格情報が変更されたユーザーが停止します。

定数の使用role_id をセッション (または memcache) に保存できます。しかし、それらを定数と比較してください。たとえばuser.role_id === ROLE_ADMIN 、db から role_ids を検索することで、実行時に定数を設定できます。これは、初期化システムで Rails プロセスごとに 1 回実行してください。受信リクエストごとに1回ではありません。

于 2012-04-24T04:27:25.383 に答える