Skip to content
better locking
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.


Better locking for ActiveRecord.


gem install barricade

Don’t forget to add it to your environment.rb or Gemfile.


ActiveRecord provides the lock! method, but it's not very robust for anything beyond really simple locks.

This plugin provides a couple of useful methods:

Post.transaction_with_locks(post, user) do

This starts a new transaction, and immediately locks all the passed in objects.

The transaction MUST be the outermost transaction, not a nested transaction, otherwise transaction_with_locks will raise a Barricade::LockMustBeOutermostTransaction exception.

It sorts the locked objects before locking, to help avoid deadlocks. If a deadlock does occur, it retries the locks and continues with the transaction.

Within the transaction block, you can raise a Barricade::RetryTransaction exception to retry the transaction from the beginning and make sure all the locks are in place.

You can double-check that you have a lock on an object by calling its confirm_locked! method. This raises a Barricade::LockNotHeld exception if you don't have the lock.

It's safe to re-acquire a lock inside an existing transaction, so the following will work:

Post.transaction_with_locks(post) do
  Post.transaction_with_locks(post) do

but this will raise an exception:

Post.transaction_with_locks(post) do
  Post.transaction_with_locks(user) do


There are a few things you need to know to understand the difficulty of using ActiveRecord for locking.

InnoDB is pretty good at detected and flagging deadlocks. (See Deadlock Detection and Rollback)

Any attempt to grab an exclusive lock on a record can result in a deadlock, and a deadlock causes a couple of things to happen:

  1. MySQL will roll back the outermost current transaction.

  2. ActiveRecord will throw a ActiveRecord::StatementInvalid exception

The end result is that your ActiveRecord objects may end up out of sync with their corresponding database records.

Barricade avoids this by doing all the locking at the very start of the transaction. A deadlock when grabbing the locks will cause an immediate retry, before any code that has side effects can be run.

The downside is that you have to do your locking in the outermost transaction, which can make it difficult to encapsulate logic in your model without placing restrictions on where your model can be called from.


Copyright © 2010 Envato. Initially developed by Pete Yandell. Released under an MIT license.

You can’t perform that action at this time.