2016-08-11 3 views
0

동일한 엔티티 (사용자)에 대해 여러 개의 Resque 작업자가 있습니다. 처리가 성공적으로 완료되면 call_left 속성이 감소해야합니다.ActiveJob/Resque 더티 읽기. 트랜잭션 격리 수준

(결과적으로) perform_now과 완벽하게 작동하지만 perform_later (예상치 못한 결과) (병렬로). 로그에는 calls_left과 동일한 수의 커밋이 있습니다.

나는 reload 메서드를 사용하려고 시도하고 심지어 가장 높은 격리 수준을 설정했습니다. 그러나 여전히이 문제가 있습니다.

해결 방법은 무엇입니까?

class DataProcessJob < ActiveJob::Base 
    queue_as :default  
    def perform(user_id, profile_id) 
    User.transaction(isolation: :serializable) do 
     user = User.find(user_id).reload 
     user.data_process(profile_id) 
     user.update(calls_left: user.calls_left-1) 
    end 
    end 
end 

답변

1

첫 번째 옵션은 (optimistic 또는 pessimistic) 잠금 을 사용하는 것입니다. 문서에 차이점이 설명되어 있으며 자신의 케이스에 맞는 것을 선택할 수 있습니다. 또한 here is a relevant code snippet 문서에서 낙관적 인 잠금 기능을 사용하면 도움이 될 것입니다.

def with_optimistic_retry 
    begin 
    yield 
    rescue ActiveRecord::StaleObjectError 
    begin 
     # Reload lock_version in particular. 
     reload 
    rescue ActiveRecord::RecordNotFound 
     # If the record is gone there is nothing to do. 
    else 
     retry 
    end 
    end 
end 

두 번째 옵션은 원시 SQL 문자열 쿼리를 사용하여 calls_left 필드를 증가하는 것입니다. 기본 DB는 원자 적 업데이트를 처리합니다.


마지막으로,하지만 적어도, 당신은 당신의 코드를보다 쉽게 ​​읽을 수 있도록 decrement!(:calls_left) 방법을 사용할 수 있습니다.

+0

잠금이 작동하지 않습니다. 낙관적 인 것은 단일 프로세스에서 작동하도록 설계 되었기 때문에 작동하지 않습니다. 비관적 인 것 - 그냥하지 않습니다. 원시 SQL이 작동하는 것 같습니다. 감사합니다. –

+0

사실, 레일즈는 테이블에'lock_version' 필드를 추가하여 낙관적 인 잠금을 구현합니다 (DB 레벨 낙관적 잠금과는 약간 다릅니다). 따라서 SQL 업데이트 쿼리에서 잠금 버전 검사를 구현하면 비 단일 프로세스 시나리오에서 작동합니다. 그러나, 나는 확실하지 않다, 소스 코드를 들여다 보지 않았다. 낙관적 잠금을 사용해 보셨습니까? 네가 해줬고 효과가 없다면 알려줘. 미래를 위해 이것을 메모하는 것이 좋을 것입니다. – Uzbekjon

+0

낙관적 잠금은 기본적으로 설정되어 있습니다. 필자의 경우 object가 시대에 뒤진 경우'Rescue ActiveRecord :: StaleObjectError'를 발생시킵니다. 그리고이 문서의 내용입니다 : '이 잠금 메커니즘은 단일 Ruby 프로세스 내에서 작동합니다. 모든 웹 요청에서 작동되게하려면 lock_version을 양식에 숨겨진 필드로 추가하는 것이 좋습니다 .' –