2012-10-12 3 views
0

사람들이 항목을 등록하는 앱이 있습니다. 각 항목에는 제한된 수의 슬롯이 있습니다. 어떻게 동시성을 처리 할 수 ​​있습니까? Item 클래스에서 다음과 같이 시도했습니다.ActiveRecord의 동시성 On

def sign_up(signup) 
    ActiveRecord::Base.transaction do 
    return 'Sorry, that item is full.' if full? 
    signups << signup 
    sheet.save! 
    nil 
    end 
end 

def full? 
    locked_signups = signups.lock(true).all 
    locked_signups.size >= max_signups 
end 

AR을 통해 가능한 작업을 수행하려고합니까? 열을 통해 자체 잠금을 구현해야합니까? 어떤 제안이라도 환영합니다.

업데이트 : 나는 태드 먼의 대답에 따라이 작업을했습니다. 다음은 작동하는 코드입니다.

rows_updated = ActiveRecord::Base.transaction do 
    Item.connection.update "update items set signup_count=signup_count+1 where id=#{ActiveRecord::Base.sanitize(self.id)} and signup_count<quantity" 
end 
return 'Sorry, that item is full. Refresh the page to see what\'s still open.' if rows_updated < 1 

답변

1

나는 신뢰할 수있는 이런 종류의 문제에 대해 두 가지 접근법을 생각할 수 있습니다.

카운터 열

당신은 "남아있는 재고"열을 만들고 원자를 업데이트 할 수 있습니다 :

UPDATE sheet SET signups_remaining=signups_remaining-:count WHERE id=:id AND signups_remaining>=:count 

당신은 그에 따라 :count:id 값에 바인딩해야합니다. 이 쿼리가 실행되면 충분한 수의 가입이 남아 있음을 의미합니다.

예약 가입 절차

을 사전에 가입 기록을 생성하고 할당 :

UPDATE signups SET allocation_id=:allocation_id WHERE allocation_id IS NULL LIMIT :count 

이 0 개 이상의 가입 레코드를 업데이트 할 것이다, 당신은 당신이 올바른 예약 확인해야합니다 있도록 거래를하기 전에 계산하십시오.

+0

어느 옵션을 사용하더라도 동일한 동시성 문제가 발생하지 않습니까? 두 번째 (세 번째, 네 번째 등) 사용자는 이전 사용자가 슬롯 (새 서명 행을 삽입 함)을 청구하고 signups_remaining에 대한 업데이트가 커밋되기 전에 signups_remaining 값을 읽습니다. –

+0

두 가지 경우 모두 신뢰할 수 있습니다. 첫 번째 경우에는 부족한 재고가 있으면 쿼리가 실행되지 않고 두 번째 경우에는 현재 할당되지 않은 것만을 요구하기 때문입니다. 'WHERE allocation_id is NULL'은 이전 작업을 덮어 쓰지 않도록합니다. 이는 실행 또는 실행되지 않는 단일 원자 명령문이기 때문에 가능합니다. 두 개의 개별 쿼리는 동기화하기가 훨씬 더 어렵습니다. – tadman