2013-08-16 17 views
7

Próbuję walczyć z niektórymi sprawami z moim menedżerem zadań w tle. Zasadniczo posiadam obiekt Thing (już istnieje) i przypisuję mu kilka właściwości, a następnie zapisz go. Po zapisaniu z nowymi właściwościami, umieszczam je w kolejce w Resque, przekazując identyfikator.Uruchomienie kodu rails po zatwierdzeniu aktualizacji bazy danych, bez after_commit

thing = Thing.find(1) 
puts thing.foo # outputs "old value" 
thing.foo = "new value" 
thing.save 
ThingProcessor.queue_job(thing.id) 

Zadanie w tle spowoduje załadowanie obiektu z bazy danych za pomocą Thing.find(thing_id).

Problem polega na tym, że znaleźliśmy Resque tak szybko w pobieraniu pracy i ładowaniu obiektu Thing z ID, że ładuje nieaktualny obiekt. Tak więc w ramach zadania wywołanie thing.foo nadal będzie zwracało "starą wartość", np. 1/100 (nie rzeczywiste dane, ale nie zdarza się to często).

Wiemy, że jest to przypadek wyścigowy, ponieważ szyny powracają z thing.save, zanim dane rzeczywiście zostaną zatwierdzone do bazy danych (w tym przypadku postgresql).

Czy istnieje sposób w Railsach do wykonywania kodu PO operacji bazy danych zatwierdza? Zasadniczo chcę się upewnić, że zanim Resque załaduje obiekt, otrzyma najświeższy obiekt. Wiem, że można to osiągnąć za pomocą haka after_commit w modelu Thing, ale nie chcę go tam. Potrzebuję tego tylko w tym konkretnym kontekście, nie za każdym razem, gdy model zatwierdza się do DB.

Odpowiedz

5

Możesz również wprowadzić transakcję. Podobnie jak w poniższym przykładzie:

transaction do 
    thing = Thing.find(1) 
    puts thing.foo # outputs "old value" 
    thing.foo = "new value" 
    thing.save 
end 
ThingProcessor.queue_job(thing.id) 

Aktualizacja: tam jest skarb, który wywołuje Po Transakcji, z tym może rozwiązać Twój problem. Oto link: http://xtargets.com/2012/03/08/understanding-and-solving-race-conditions-with-ruby-rails-and-background-workers/

+0

Więc uruchomienie transakcji zablokuje wykonanie 'ThingProcessor.queue_job (thing.id)' dopóki wszystkie zapytania w bloku transakcji nie zostaną zatwierdzone? Myślałem, że bloki transakcyjne po prostu zapewniły, że jeśli wystąpi awaria w jakichkolwiek operacjach bazy danych w bloku, wszystko zostanie wycofane. Funkcja bazy danych. Not a ruby ​​feature. – Brian

+0

Uważam, że tak. Spróbuj i daj mi znać, jeśli działa. –

+0

Nie można znaleźć żadnej dokumentacji Railsowej, która mówi, że blokowanie wykonywania bloków transakcji. Ktoś wie na pewno? – Ben

0

Co owijania try wokół transaction tak, że praca jest w kolejce tylko po sukcesie transakcji?

+0

A czy 'thing.save' nie zwraca true tylko po pomyślnym zapisaniu? ... Czy próbowałeś również: 'ThingProcessor.queue_job (thing_id) if thing.save'. ? – user2669055

+2

Nie, "thing.save" zwraca true po tym, jak Rails AR przekazał żądanie do warstwy DB. Dlatego wywołania zwrotne after_save mogą mieć problemy z kondycją wyścigu i powody, dla których istnieje call_commit, który czeka, aż DB napisa i zwróci potwierdzenie. Problem z dodawaniem warunków warunkowych lub bloków próbnych polega na tym, że zadanie nie będzie czekało w kolejce, gdy zostanie wykryty warunek wyścigu. Więcej kodu musiałoby zostać dodane, aby zakreślić ponownie i spróbować ponownie ustawić kolejkę do pracy później. To dodaje złożoności. – Ben

+0

Przepraszam, myślę, że połowa mojego poprzedniego komentarza może być błędna. Jest możliwe, że bloki transakcji * nie * blokują wykonanie, a ponieważ wywołania "zapisywania" Railsów są zawijane w bloki transakcji, wywołanie save może nie zwrócić "true" dopóki transakcja bazy danych nie zakończy się pomyślnie. Tak więc możliwe byłoby zawarcie warunkowe. Ale nie rozwiązuje problemu konieczności znalezienia sposobu na ponowne ustawienie pracy. – Ben

Powiązane problemy