Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Run rails code after an update to the database has commited, without after_commit

I'm trying to battle some race cases with my background task manager. Essentially, I have a Thing object (already exists) and assign it some properties, and then save it. After it is saved with the new properties, I queue it in Resque, passing in the ID.

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

The background job will load the object from the database using Thing.find(thing_id).

The problem is that we've found Resque is so fast at picking up the job and loading the Thing object from the ID, that it loads a stale object. So within the job, calling thing.foo will still return "old value" like 1/100 times (not real data, but it does not happen often).

We know this is a race case, because rails will return from thing.save before the data has actually been commit to the database (postgresql in this case).

Is there a way in Rails to only execute code AFTER a database action has commit? Essentially I want to make sure that by the time Resque loads the object, it is getting the freshest object. I know this can be achieved using an after_commit hook on the Thing model, but I don't want it there. I only need this to happen in this one specific context, not every time the model has commit changed to the DB.

like image 539
Brian Avatar asked Aug 16 '13 18:08

Brian


1 Answers

You can put in a transaction as well. Just like the example below:

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

Update: there is a gem which calls After Transaction, with this you may solve your problem. Here is the link: http://xtargets.com/2012/03/08/understanding-and-solving-race-conditions-with-ruby-rails-and-background-workers/

like image 57
Edilson Borges Avatar answered Oct 16 '22 15:10

Edilson Borges