2010-04-15 142 views
0

我正在構建一個基於Sinatra的應用程序以部署在Heroku上。你可以把它想象成一個標準的URL縮寫,但舊的短代碼到期並可用於新的URL(我認識到這是一個愚蠢的概念,但它更容易解釋這種方式)。我將我的數據庫中的簡碼作爲一個整數表示,並重新定義它的閱讀器,從整數中給出一個簡短而唯一的字符串。數據庫鎖定:ActiveRecord + Heroku

由於某些行將被刪除,我已經編寫了所有短代碼整數的代碼,然後選擇第一個空的代碼來使用before_save。不幸的是,如果我一個接一個地快速運行兩個實例,那麼我可以讓我的代碼創建具有相同簡碼整數的兩行,這顯然是不好的!我應該如何實現一個鎖定系統,以便我可以使用唯一的簡碼整數快速保存我的記錄?

這是我到目前爲止有:

Chars = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a 
CharLength = Chars.length 

class Shorts < ActiveRecord::Base 
    before_save :gen_shortcode 
    after_save :done_shortcode 

    def shortcode 
    i = read_attribute(:shortcode).to_i 

    return '0' if i == 0 
    s = '' 
    while i > 0 
     s << Chars[i.modulo(CharLength)] 
     i /= 62 
    end 
    s 
    end 

    private 
    def gen_shortcode 
    shortcode = 0 
    self.class.find(:all,:order=>"shortcode ASC").each do |s| 
     if s.read_attribute(:shortcode).to_i != shortcode 
     # Begin locking? 
     break 
     end 
     shortcode += 1 
    end 

    write_attribute(:shortcode,shortcode) 
    end 

    def done_shortcode 
    # End Locking? 
    end 
end 

回答

4

這條線:

self.class.find(:all,:order=>"shortcode ASC").each 

會做在您的整個記錄​​集順序搜索。您必須鎖定整個表格,以便在其中一個進程正在掃描下一個整數時,其他進程將等待第一個整數完成。這將是一個性能殺手。我的建議,如果可能的話,對於流程如下:(?你的創作最後使用時間到期它們)

  1. 增加一列,指示何時記錄已過期。索引此欄。
  2. 當你需要找到下一個最低可用數量,這樣做

    Shorts.find(:條件=> {:過期=>真},:爲了=> '短碼')

這將使數據庫盡最大努力找到最短的過期短代碼。回想一下,在沒有:全部參數的情況下,找到方法將只返回第一個匹配的記錄。現在

,爲了防止進程之間的競爭條件,你可以在一個交易把這個包,並鎖定在做搜索:

Shorts.transaction do 
    Shorts.find(:conditions => {:expired => true},:order => 'shortcode', :lock => true) 
    #Do your thing here. Be quick about it, the row is locked while you work. 
end #on ending the transaction the lock is released 

現在,當第二個進程開始尋找免費的短代碼,它會不是讀取鎖定的那個(所以推測它會找到下一個)。這是因爲:lock => true參數獲得排他鎖(讀/寫)。

檢查this guide瞭解有關使用ActiveRecord鎖定的更多信息。