# Validate not occurring transactionally

**URL:** <https://www.ruby-forum.com/t/validate-not-occurring-transactionally/156682>\
**Category:** Rails\
**Created:** [December 31, 2008, 10:05pm UTC](https://www.ruby-forum.com/t/validate-not-occurring-transactionally/156682 "2008-12-31T22:05:26Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [December 31, 2008, 10:05pm UTC](https://www.ruby-forum.com/t/validate-not-occurring-transactionally/156682/1 "2008-12-31T22:05:26Z")

</div>

I’m running into a strange problem with transactions. I am seeing  
rare duplicate entries in the DB that should be prevented by my  
validations. Here is some sample code:

class Review \< ActiveRecord::Base  
has\_one :member  
has\_one :business  
attr\_accessor :sleep\_for

def validate  
validate\_uniqueness\_combo\_of\_member\_and\_business  
sleep(sleep\_for) if self.sleep\_for  
end  
end

and here is that code executed:

#console 1

> r = Review.new(:member\_id =\> 1, :business\_id =\> 4)  
> r.sleep\_for = 5  
> r.save!  
> =\> true

#console 2 (during the 5 sec sleep)

> r = Review.new(:member\_id =\> 1, :business\_id =\> 4)  
> r.save!  
> =\> true

I would expect the first save to lock the table and the second to fail  
after 5 seconds. Instead they both succeed and I end up with bad data  
in the db. It also fails if I wrap both save! calls in  
Review.transaction blocks.

I could not find docs on whether validate() is within the transaction,  
but I would expect it to be. Any ideas on what is wrong here?

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [December 31, 2008, 11:59pm UTC](https://www.ruby-forum.com/t/validate-not-occurring-transactionally/156682/2 "2008-12-31T23:59:30Z")

</div>

Validations about uniqueness of things are prone to race conditions.  
If you need a cast iron guarantee that can only come from the database  
itself (ie unique index etc…)

Sent from my iPhone

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 1, 2009, 12:31am UTC](https://www.ruby-forum.com/t/validate-not-occurring-transactionally/156682/3 "2009-01-01T00:31:05Z")

</div>

On Dec 31, 2:58 pm, Frederick C. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> Validations about uniqueness of things are prone to race conditions.  
> If you need a cast iron guarantee that can only come from the database  
> itself (ie unique index etc…)

The app allows an “anonymous” user to have multiple reviews of a  
business, so a DB level constraint probably isn’t appropriate. But  
regardless, my question is about what I can/can’t expect from AR with  
respect to transactions/validations. If validations aren’t within the  
transaction they’re not useful. Since I don’t believe the Rails folks  
are stupid, I’m trying to figure out what I’m doing wrong here.

Thanks,

pt.

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 1, 2009, 3:35am UTC](https://www.ruby-forum.com/t/validate-not-occurring-transactionally/156682/4 "2009-01-01T03:35:50Z")

</div>

unknown wrote:

> #console 1
> 
> > r = Review.new(:member\_id =\> 1, :business\_id =\> 4)  
> > This line does absolutely nothing at the database level. All you have  
> > done here is make a new Ruby object.

> > r.sleep\_for = 5  
> > r.save!  
> > This row create a database transaction, writes the data from the Ruby  
> > object’s attributes and ends the transaction.

> =\> true
> 
> #console 2 (during the 5 sec sleep)
> 
> > r = Review.new(:member\_id =\> 1, :business\_id =\> 4)  
> > Again the database know nothing about the creation of the Ruby object.

> > r.save!  
> > Since this executes before you have saved the first record, and  
> > validations for both records have already occurred your uniqueness  
> > validation WILL fail and allow a duplicate to be recorded in the  
> > database.

> =\> true
> 
> I would expect the first save to lock the table and the second to fail  
> after 5 seconds. Instead they both succeed and I end up with bad data  
> in the db. It also fails if I wrap both save! calls in  
> Review.transaction blocks.  
> Locking the table in this scenario would be death to scalability. What  
> most people do in this case is to use the validates\_uniqueness\_of for  
> convenience, but also ensure data integrity by adding the proper unique  
> index to the database.

A fail-and-recover scheme is what is needed here. As much as table  
locking sounds like the way to go, it just isn’t because it’s way to  
expensive.

I also understand your issue with anonymous users (especially since you  
yourself have chosen to be an anonymous coward on this forum – kidding  
a little there hehe). But, that’s just something you’ll have to weigh  
for yourself.

As Frederick has already mentioned: race conditions are a tough problem  
to solve and are best done at the database layer. They are a lot more  
difficult to solve at the model object layer, which is the layer where  
validations are performed). This, however, does not render validations  
worthless. AFAIK uniqueness validations are the only ones that suffer  
from this issue.

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 1, 2009, 3:43am UTC](https://www.ruby-forum.com/t/validate-not-occurring-transactionally/156682/5 "2009-01-01T03:43:25Z")

</div>

Oh! And BTW this issue is by no means unique to Rails. I’ve had to deal  
with this issue in every ORM I’ve ever come across. Most ORMs don’t even  
attempt to provide something “built-in” to help deal with this. In most  
of them you have to develop your own solution anyway.

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [January 1, 2009, 10:01am UTC](https://www.ruby-forum.com/t/validate-not-occurring-transactionally/156682/6 "2009-01-01T10:01:30Z")

</div>

On 31 Dec 2008, at 23:30, removed\_email\_address@domain.invalid wrote:

> regardless, my question is about what I can/can’t expect from AR with  
> respect to transactions/validations. If validations aren’t within the  
> transaction they’re not useful. Since I don’t believe the Rails folks  
> are stupid, I’m trying to figure out what I’m doing wrong here.

It should be easy to work out whether they run in a transaction or not  
by looking at the log files. Most validations are just about the one  
object and so couldn’t care less. Even if they are it wouldn’t help  
your uniqueness validations (unless you’re actually locking rows)

Fred
