Transaction & Lock

Transaction

Transaction mechanism provides block-like syntax:

Jennifer::Adapter.default_adapter.transaction do |tx|
  Contact.create({:name => "Chose", :age => 20})
end

If any error was raised in block transaction will be rollbacked. To rollback transaction raise DB::Rollback exception.

Transaction lock connection for current fiber avoiding grepping new one from pool.

Lock

Row level

Provides support for row-level locking using SELECT … FOR UPDATE and other lock types.

Chain #find to #lock to obtain an exclusive lock on the selected rows:

# select * from contacts where id=1 for update
Contact.all.lock.find(1)

You can also use Jennifer::Model::Base#lock! method to lock one record by id. This may be better if you don’t need to lock every row. Example:

Contact.transaction do
  # select * from contacts where ...
  contacts = Contact.where { _age > 15 }.to_a
  contact = contacts.find { |c| c.name =~ /John/ }.not_nil!
  contact.lock!
  contact.age += 10
  contact.save!
end

You can start a transaction and acquire the lock in one go by calling with_lock with a block. The block is called from within a transaction, the object is already locked. Example:

contact = Contact.first!
contact.with_lock do
  # This block is called within a transaction,
  # record is already locked.
  contact.age += 100
  contact.save!
end

Table level

To lock table use Jennifer::Adapter#with_table_lock method

Jennifer::Adapter.default_adapter("table_name") do
  # some operations here
end

Or performing directly on model class:

Contact.with_table_lock do
  # some operations here
end

But only postgres adapter supports the real table LOCK statement - mysql one just wraps a call to the transaction. This is caused by performing queries with prepared statements and mysql doesn’t allow lock table via it.

Database-specific information on row locking: