# Proper way to ensure atomic model changes

**URL:** <https://www.ruby-forum.com/t/proper-way-to-ensure-atomic-model-changes/168300>\
**Category:** Rails\
**Created:** [May 19, 2009, 10:57am UTC](https://www.ruby-forum.com/t/proper-way-to-ensure-atomic-model-changes/168300 "2009-05-19T10:57:18Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![kuvkir](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/k/85f322/32.png) [@kuvkir](https://www.ruby-forum.com/u/kuvkir)\
**Post date:** [May 19, 2009, 10:57am UTC](https://www.ruby-forum.com/t/proper-way-to-ensure-atomic-model-changes/168300/1 "2009-05-19T10:57:18Z")

</div>

Hi!

Say we have a rails active-record-based model called “instance” which  
can have different states - STOPPED, RUNNING, STARTING\_UP,  
SHUTTING\_DOWN, etc…

There is a method #start which changes the instance state to  
STARTING\_UP only if it is STOPPED.

def start  
if self.state == STOPPED  
self.state = STARTING\_UP  
self.save  
…  
end  
end

As you understand if we have multiple concurrent requests for #start,  
there is a possibility of a race condition, where two or more  
processes are running the if branch.

I want the #start request to be atomic, meaning that only one of the  
concurrent processes can actually execute the if branch, others must  
be waiting or get some kind of error.

Should the request be somehow wrapped into a DB transaction?

-Kirill

---

<div class="post-metadata">

**Author:** ![kuvkir](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/k/85f322/32.png) [@kuvkir](https://www.ruby-forum.com/u/kuvkir)\
**Post date:** [May 19, 2009, 11:41am UTC](https://www.ruby-forum.com/t/proper-way-to-ensure-atomic-model-changes/168300/2 "2009-05-19T11:41:13Z")

</div>

On 19 May 2009, at 09:56, removed\_email\_address@domain.invalid wrote:

> 

take a look at optimistic locking.

Fred

---

<div class="post-metadata">

**Author:** ![kuvkir](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/k/85f322/32.png) [@kuvkir](https://www.ruby-forum.com/u/kuvkir)\
**Post date:** [May 19, 2009, 12:25pm UTC](https://www.ruby-forum.com/t/proper-way-to-ensure-atomic-model-changes/168300/3 "2009-05-19T12:25:12Z")

</div>

On Tuesday 19 May 2009, removed\_email\_address@domain.invalid wrote:

> self.save  
> be waiting or get some kind of error.
> 
> Should the request be somehow wrapped into a DB transaction?

Yes, that at any rate. However, don’t confuse transaction blocks with  
multiple exclusion blocks. A transaction, by itself, doesn’t preclude  
concurrent users/processes from reading and updating the same data. The  
potential for conflict arises only through concurrent updates.

The race condition in your unadorned code results from a difference  
between time of check (state == x) and time of change (state = …).  
There are two strategies to avoid the resulting inconsistencies:

Optimistic locking, as Fred indicated in another reply. With optimistic  
locking, you run headlong into the race condition, but when writing to  
the database you ensure that it can only succeed if is based on  
consistent data. On updating a record, ActiveRecord checks that the  
updated\_at timestamp of the record as currently stored is the same as  
the timestamp when the object was read. An identical timestamp indicates  
that there haven’t been any intervening updates.

Pessimistic locking is another way. You can either #lock! an object you  
already have or #find(…, :lock =\> true) get a locked object to begin  
with. Locking an object like this precludes any changes to the  
corresponding database row until the locking transaction is either  
committed or rolled back.

Rails gives you optimistic locking automatically for tables that have  
the requisite timestamp columns (updated\_at). Pessimistic locking you  
have to do explicitly. As a guess, I’d say that pessimistic locking is  
only worth your and the database’s effort if conflicts are likely.

At any rate, with both locking strategies you have to take into account  
the possibility of a conflict. With optimistic locking, you get an  
ActiveRecord::StaleObjectError exception in that case. I’m not sure  
about pessimistic locking, but I guess you’ll get an indistinctive  
ActiveRecord::StatementInvalid exception.

Michael

–  
Michael S.  
[mailto:removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
[http://www.schuerig.de/michael/](http://www.schuerig.de/michael/)

---

<div class="post-metadata">

**Author:** ![kuvkir](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/k/85f322/32.png) [@kuvkir](https://www.ruby-forum.com/u/kuvkir)\
**Post date:** [May 19, 2009, 12:51pm UTC](https://www.ruby-forum.com/t/proper-way-to-ensure-atomic-model-changes/168300/4 "2009-05-19T12:51:01Z")

</div>

On May 19, 11:24 am, Michael S. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Rails gives you optimistic locking automatically for tables that have  
> the requisite timestamp columns (updated\_at). Pessimistic locking you  
> have to do explicitly. As a guess, I’d say that pessimistic locking is  
> only worth your and the database’s effort if conflicts are likely.

You mean lock\_version (integer, default 0)

> At any rate, with both locking strategies you have to take into account  
> the possibility of a conflict. With optimistic locking, you get an  
> ActiveRecord::StaleObjectError exception in that case. I’m not sure  
> about pessimistic locking, but I guess you’ll get an indistinctive  
> ActiveRecord::StatementInvalid exception.

If you hold a lock then anyone trying to update/select that row will  
wait until you release your lock (or until they give up waiting).

Fred

---

<div class="post-metadata">

**Author:** ![kuvkir](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/k/85f322/32.png) [@kuvkir](https://www.ruby-forum.com/u/kuvkir)\
**Post date:** [May 19, 2009, 1:09pm UTC](https://www.ruby-forum.com/t/proper-way-to-ensure-atomic-model-changes/168300/5 "2009-05-19T13:09:14Z")

</div>

On Tuesday 19 May 2009, Frederick C. wrote:

> On May 19, 11:24 am, Michael S. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:
> 
> > Rails gives you optimistic locking automatically for tables that  
> > have the requisite timestamp columns (updated\_at). Pessimistic  
> > locking you have to do explicitly. As a guess, I’d say that  
> > pessimistic locking is only worth your and the database’s effort if  
> > conflicts are likely.
> 
> You mean lock\_version (integer, default 0)

Yes, of course.

> > At any rate, with both locking strategies you have to take into  
> > account the possibility of a conflict. With optimistic locking, you  
> > get an ActiveRecord::StaleObjectError exception in that case. I’m  
> > not sure about pessimistic locking, but I guess you’ll get an  
> > indistinctive ActiveRecord::StatementInvalid exception.
> 
> If you hold a lock then anyone trying to update/select that row will  
> wait until you release your lock (or until they give up waiting).

And if they give up waiting that manifests itself in some kind of  
error/exception that needs to be handled.

Locking out other readers (SELECT) through SELECT … FOR UPDATE is the  
default locking mode used by ActiveRecord when :lock =\> true is used.  
Nevertheless, the mode can be explicitly specified to allow concurrent  
readers, but block writers.

PostgreSQL: :lock =\> ‘FOR SHARE’  
MySQL: :lock =\> ‘LOCK IN SHARE MODE’

Michael

–  
Michael S.  
[mailto:removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
[http://www.schuerig.de/michael/](http://www.schuerig.de/michael/)
