PostgreSQL 9.0에서 실행되는 Rails 3 프로젝트가 있습니다.ActiveRecord : 작업자 간의 DB 경쟁 처리
사용 사례 : 사용자는 Artists
의 이름을 따라달라고 요청할 수 있습니다. 이렇게하기 위해 REST 리소스에 이름 목록을 제출합니다. 로컬 컬렉션에서 Artist
의 이름을 찾을 수 없으면 last.fm에 대한 정보를 문의하고 해당 정보를 로컬에 캐시합니다. 이 프로세스는 다소 시간이 걸릴 수 있으므로 IndexArtistJob
이라는 백그라운드 작업에 위임됩니다.
문제 : IndexArtistJob
이 병렬로 실행됩니다. 따라서 두 명의 사용자가 동일한 Artist
을 동시에 추가하도록 요청할 수 있습니다. 두 사용자 모두 해당 컬렉션에 Artist
을 추가해야하지만 Artist
중 하나만 로컬 데이터베이스에 저장되어야합니다. Artist
모델의
관련 부분은 다음과 같습니다
class IndexArtistJob < Struct.new(:user_id, :artist_name)
def perform
user = User.find(user_id)
# May return a new, uncommitted Artist model, or an existing, committed one.
artist = Artist.lookup(artist_name)
return if artist.nil?
# Presume the thread is pre-empted here for a long enough time such that
# the work done by this worker violates the DB's unique constraint.
user.artists << artist
rescue ActiveRecord::RecordNotUnique # Lost race, defer to winning model
user.artists << Artist.lookup(artist_name)
end
end
내가 여기서 할 노력하고있어하는 것은 각 작업자가 새로운 Artist
을 저지 할 수있다 :로
require 'services/lastfm'
class Artist < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name, :case_sensitive => false
def self.lookup(name)
artist = Artist.find_by_name(name)
return artist if not artist.nil?
info = LastFM.get_artist_info(name)
return if info.nil?
# Check local DB again for corrected name.
if name.downcase != info.name.downcase
artist = Artist.find_by_name(info.name)
return artist if not artist.nil?
end
Artist.new(
:name => info.name,
:image_url => info.image_url,
:bio => info.bio
)
end
end
IndexArtistJob
클래스 정의 발견, 최선을 희망합니다. 충돌이 발생하면 느린 작업자가 방금 삽입 한 Artist
에 대한 작업을 포기하고 지정된 사용자에게 Artist
을 추가합니다.
레일스 검사기가 데이터베이스 수준에서 실제 데이터 무결성 검사를 대신 할 수 없다는 사실을 알고 있습니다. 이를 처리하기 위해 아티스트 테이블의 소문자 이름 필드에 고유 인덱스를 추가하여이를 처리했습니다 (검색에 사용). 이제 설명서를 올바르게 이해하면 AR의 연관 수집이 추가되는 항목 (이 경우 Artist
)과 트랜잭션의 기본 모음에 대한 변경 사항을 커밋합니다. 그러나 나는 보장 할 수 없다. Artist
이 추가 될 것이다.
제대로하고 있습니까? 그렇다면 더 좋은 방법이 있습니까? 예외를 중심으로 구조화하는 것이 문제가 동시성의 하나임을 강조하고 약간의 미묘함을 느끼는 것처럼 느껴집니다.
필자는 큐에 대해 SimpleWorker 또는 delayed_job/Resque를 검토 할 것이라고 언급 했어야합니다. 당신의 솔루션은 대기열을 일종의 뮤텍스로 사용합니다. 이것은 매우 영리합니다. 그러나 나는 바퀴를 다시 발명 할 수 있을지 확신하지 못합니다. –
전적으로 동의합니다 ... 세계에는 충분한 바퀴가 있습니다. – scaganoff