2012-03-02 15 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

+0

私はmysqlを使用しています – jsharpe

+0

'LOCK IN SHARE MODE'を使用してください。手巻きのソリューションを投げ捨て、http://guides.rubyonrails.org/active_record_querying.html#pessimistic-locking – meagar

答えて

0

こんにちは、

私はちょうど1つの警告でかなり簡単SQL-アプローチを想像:list_positionのようなものをと呼ばれる追加の列を追加し、classroom_idからなる教室の生徒-関係テーブルに一意のインデックスを追加、student_idおよびlist_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 

の検索結果を次のようになります。2つの挿入が同時に同じクラスに学生を挿入しようとした場合、一意のインデックスは、(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

関連する問題