« HE:labs

Thread-safing your objects with the Lockable concern

Postado por Thiago Belem em 25/04/2014

Sometimes, you just want to make your database objects threadsafe and run parallel/concurrent tasks on them without making a mess.

Imagine the following situation:

  • You have a background job (cheers, Sidekiq!) that will run and update/modify a list of database records based on some logic.
  • You have more than one of this job running at the same time.

What would happend if two jobs want to update the same record, at the same time?

Wouldn't be lovely if the 2nd job skips the first record if the 1st job is working on it?

Thats why I created the following concern:

 1 # app/models/concerns/lockable.rb
 3 require 'active_support/concern'
 5 module Lockable
 6   @locked = false
 8   def locked?
 9     @locked == true
10   end
12   def lock!
13     raise Exception, 'You should define the lock! method inside your class'
14   end
16   def unlock!
17     raise Exception, 'You should define the unlock! method inside your class'
18   end
20   def while_locked
21     return false if locked?
23     lock!
24     needs_unlock = true
26     yield
27   ensure
28     unlock! if needs_unlock
29   end
30 end

Here is the Lockable concern specs if you're wondering: gist.github.com/TiuTalk/10433564

Remember to override the persistence methods on your model, like this:

 1 class MyModel < ActiveRecord::Base
 2   include Lockable
 4   def lock!
 5     update_attribute(:locked, true)
 6   end
 8   def unlock!
 9     update_attribute(:locked, false)
10   end
11 end

And you can wrap your code with the while_locked method, like this:

1 object = MyModel.find(12)
3 object.while_locked do
4   object.some_serious_shit!
5 end

With this, you can have multiple simultaneous processes running on your database records without one messing with the other! How fancy is that?

See ya'll!


Sabia que nosso blog agora está no Medium? Confira Aqui!