Get a Unique Integer
Get a Unique Integer in Database Level
需求
剛好在前後公司看到類似的需求,因為需要除了Pirmary Key之外的整數來當作給人閱讀用的ID,原先的作法是
1 | # 資料record,有一個欄位integer |
實際上這個作法有兩個問題
最直覺的是當6位數用掉大部份之後,為了找到還沒用過的數字會在while loop裡面重覆很多次,理論上用光之後會陷入無窮迴圈。
另一個是其實Rails uniqueness validation驗証資料是否在,其實是分成兩個動作
1) Record.exist? 2) Record.save
所以如果1的結果回傳false,但在production的環境下,有另一個instance也在做一樣的事情,這個時候就有可能會存兩筆一樣的資料。可以參考ThoughtBots這篇文章
作法
之前是用mysql,我唯一能想到的方法是在迴圈中,如果驗証失敗的話,就增加rand的位數,這樣可以避免上述第一個問題。不過並不算是完整的解決問題。
後來這家公司是用postgresql,而有提供SEQUENCE功能,利用這個功能在database level上我們就可以取得一個unique integer。
1 | # migration |
會在postgresql中會建立一個叫做ingeter
的sequence。因為這不是Rails原生的功能,必需用up/down
來自行控制migration/rollback
接著在model內要取得unique integer時,就可以
1 | def get_unique_integer |
每一次SELECT nextval('integer');
都會拿到下一個數字,所以就能夠確保拿到的是unique integer。
利用before_validation
來自動填入
1 | before_validation: :set_integer |
然後human_readable_id
這個欄位,記得再上index unique確保存入database時的確是unique的。
優缺點
這樣做上述的兩個問題都得到了解答,不過仍要注意的是,這個作法並不能保証取得的是連續
的整數,因為只要呼叫nextval
數字就會跳號,它並不會知道你有沒有使用這個數字。也就是說如果出現rollback數字還是會跳號。