2018年5月23日 星期三

Devise 每次都會同時下 ORDER BY 和 LIMIT 的 sql 去找 users


同事提出的建議:

```
SELECT "users".* FROM "users" WHERE "users"."id" = $? ORDER BY "users"."id" ASC LIMIT $?

id 是 primary key, 理論上不會有重複的資料.

SELECT id, COUNT(id) AS cid FROM users GROUP BY id ORDER BY cid DESC;
id | cid
--------+-----
1 | 1
58291 | 1
(以下省略)

也確認目前的資料中, 的確沒有出現重複的 id.

這個 query 的 ORDER BY 跟 LIMIT 確認不會被 pgsql 的 query plan / optimizer 省略掉, 還是會產生 procedure call 消耗些微的 cpu / memory, 所以還是建議修改這個 query.
```

查了一下主要罪魁禍首是


Devise::Models::Authenticatable.serialize_from_session

這個 method 是每次已登入的 user 訪問頁面時就會 trigger 的,裡面用了 User.to_adapter.get([id]) 的方式來拿資料

```
 :014 > User.to_adapter.get([1])
  User Load (2.8ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 1], ["LIMIT", 1]]
```

另外比較不重要但是也會影響的地方就是登入時的驗證,確切來說就是 Devise::Models::DatabaseAuthenticatable.find_for_database_authentication ,裡面用了 to_adapter.find_first

```
:005 > User.to_adapter.find_first(id: 1)
  User Load (1.7ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 1], ["LIMIT", 1]]
```

可以參考的解法:

* customize serializer: https://github.com/plataformatec/devise/wiki/How-to:-Write-your-own-serializer
* overwrite, ex:

```
class User < ApplicationRecord
  def self.serialize_from_session(key, salt)
    record = find_by(id: key)
    record if record && record.authenticatable_salt == salt
  end
end
```

* 想辦法在不必要驗證的地方 skip 上述 find user 的 middelware



沒有留言:

張貼留言