2012-03-02 18 views
3

編輯:使用MySQL ...解決在軌的競爭條件(僅限於Y X的數量)

假設你有一個應用程序,增加了學生的一類,而這個類的空間有限...所以你這樣做:

def add 
    if some_classroom.size < MAX_SIZE 
    add_student_to_class 
    end 
end 

這是一個多線程環境中的競態條件。瘸。

假設我們

  1. 不想要這個,和
  2. 不希望鎖定我們的教室表或記錄(這將導致我們的應用程序在其他地方吸)

做什麼我們的確是?

我提出這樣的:

class Classroom < ActiveRecord::Base 
    has_one :classroom_lock 
    after_create :create_lock_record 

    def create_lock_record 
    c = ClassroomLock.new 
    c.classroom = self 
    c.save! 
    end 
end 

class ClassroomLock < ActiveRecord::Base 
    belongs_to :classroom 
end 

def add 
    c = Classroom.first 
    ActiveRecord::Base.transaction do 
    c.classroom_lock.lock! 
    c = Classroom.first #load this again (it might have changed) 
    if c.size < MAX_SIZE 
     c.add_new_student(some_student) 
    else 
     do_stuff_about_not_enough_room 
    end 
    end 
end 

這似乎像它應該赫然工作。我的(虛構)Classroom#show方法不會被阻止,因爲課堂記錄實際上並未鎖定,並且add方法實際上是單線程的,因爲任何其他進程都將被迫等待鎖定!直到鎖定被釋放。

這是行不通的?也許?我想是這樣?我不知道...

我已經做了一個錘擊這一步與多個進程一次,但它很難確定(畢竟這是一個競爭條件)。

任何人都可以提供一些額外的見解嗎?

+1

您正在使用什麼數據庫? – meagar 2012-03-02 15:28:28

+0

我正在使用mysql – jsharpe 2012-03-02 15:33:26

+0

使用'LOCK IN SHARE MODE'。拋出你的手卷解決方案,看看http://guides.rubyonrails.org/active_record_querying.html#pessimistic-locking – meagar 2012-03-02 15:38:48

回答

0

嗨,

我想只有一個需要注意的一個非常簡單的SQL的方法:添加一個名爲什麼其他列像list_position,並添加一個唯一索引到由classroom_id教室學生關係表,student_idlist_position

插入比將list_position設置爲課堂上學生人數再加上1。 Ruby代碼很可能是一起地方

classroomStudent = ClassroomStudent.new 
classroomStudent.list_position = classroom.size + 1 
until classroomStudent.save 
    classroomStudent.list_position += 1 
    do_stuff_about_not_enough_room if classroomStudent.list_position > MAX_SIZE 
end 

結果:如果兩個刀片試圖學生在同一時間插入到同一類中的唯一索引將保持一個學生出來(INSERT失敗)。這意味着你可能不得不重試,直到list_position=MAX_SIZE,但保證你在課堂上永遠不會有太多的學生。 你也可以用這種方法建立一個等待隊列。

如果您的關係表中已經有數據,則必須首先爲list_position添加一個值。我猜想像

UPDATE ClassroomStudents AS c1 SET c1.list_position = COALESCE((SELECT MAX(c2.list_position) FROM ClassroomStudents AS c2 WHERE c2.classroom_id = c1.classroom_id), 0) + 1; 

會做這裏的工作,雖然它可能有點慢。

我知道解決方案仍然有一些粗糙的邊緣,但也許它有幫助。

問候

TC