<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>
<channel>
	<title>Comments on: ActiveRecord race conditions</title>
	<atom:link href="http://barelyenough.org/blog/2007/11/activerecord-race-conditions/feed/" rel="self" type="application/rss+xml" />
	<link>http://barelyenough.org/blog/2007/11/activerecord-race-conditions/</link>
	<description></description>
	<pubDate>Wed, 07 Jan 2009 16:33:05 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5</generator>
		<item>
		<title>By: Peter Williams</title>
		<link>http://barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39402</link>
		<dc:creator>Peter Williams</dc:creator>
		<pubDate>Sun, 25 Nov 2007 21:33:57 +0000</pubDate>
		<guid isPermaLink="false">http://pezra.barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39402</guid>
		<description>&lt;p&gt;Stephen,&lt;/p&gt;

&lt;p&gt;Serializable transactions would help in this case because a phantom read (the initial existence check did not return a record at the time it was run that it would return at the end of the transaction) would have occurred in all the transactions except the first one to commit.  This means that all the transactions except the first one to commit would fail in strict serializable isolation mode because a phantom read is not allowed in that mode.&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>Stephen,</p>
<p>Serializable transactions would help in this case because a phantom read (the initial existence check did not return a record at the time it was run that it would return at the end of the transaction) would have occurred in all the transactions except the first one to commit.  This means that all the transactions except the first one to commit would fail in strict serializable isolation mode because a phantom read is not allowed in that mode.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Stephen</title>
		<link>http://barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39392</link>
		<dc:creator>Stephen</dc:creator>
		<pubDate>Sun, 25 Nov 2007 18:03:28 +0000</pubDate>
		<guid isPermaLink="false">http://pezra.barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39392</guid>
		<description>&lt;p&gt;Peter/Jay,&lt;/p&gt;

&lt;p&gt;I totally understand why people would like not to put constraints in the database - it means you have to get your hands dirty with the database, which is something many application developers don't like.  The Rails community may not say 'constraints are wrong', but they imply they should make you feel dirty if you use them.&lt;/p&gt;

&lt;p&gt;This sort of race condition problem is something that just cannot be efficiently solved without DB constraints - infact, I &lt;em&gt;think&lt;/em&gt; that unless you use a lock instead (which is a horrible unscalable solution) you cannot guarantee the uniqueness of your data without DB constraints.&lt;/p&gt;

&lt;p&gt;Serializable transactions don't solve enforcing the uniqueness of a column unless there is a unique constraint involved too.&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>Peter/Jay,</p>
<p>I totally understand why people would like not to put constraints in the database - it means you have to get your hands dirty with the database, which is something many application developers don&#8217;t like.  The Rails community may not say &#8216;constraints are wrong&#8217;, but they imply they should make you feel dirty if you use them.</p>
<p>This sort of race condition problem is something that just cannot be efficiently solved without DB constraints - infact, I <em>think</em> that unless you use a lock instead (which is a horrible unscalable solution) you cannot guarantee the uniqueness of your data without DB constraints.</p>
<p>Serializable transactions don&#8217;t solve enforcing the uniqueness of a column unless there is a unique constraint involved too.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Peter Williams</title>
		<link>http://barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39287</link>
		<dc:creator>Peter Williams</dc:creator>
		<pubDate>Sat, 24 Nov 2007 21:39:24 +0000</pubDate>
		<guid isPermaLink="false">http://pezra.barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39287</guid>
		<description>&lt;p&gt;Tobi,&lt;/p&gt;

&lt;p&gt;Those two facilities don't help with this set of issues.  These race conditions relate to ending up with two separate records that should be mutually exclusive based on the validations.&lt;/p&gt;

&lt;p&gt;For example, in my case I was inserting two logically identical records  simultaneously.  Both processes that want to insert this record check for it's existence, saw that it does not exist and then insert it.  No amount of row level locking or row versioning would have helped.&lt;/p&gt;

&lt;p&gt;Further, I think it would quite easy to argue that both of those approaches are pretty kludgey in most situations.&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>Tobi,</p>
<p>Those two facilities don&#8217;t help with this set of issues.  These race conditions relate to ending up with two separate records that should be mutually exclusive based on the validations.</p>
<p>For example, in my case I was inserting two logically identical records  simultaneously.  Both processes that want to insert this record check for it&#8217;s existence, saw that it does not exist and then insert it.  No amount of row level locking or row versioning would have helped.</p>
<p>Further, I think it would quite easy to argue that both of those approaches are pretty kludgey in most situations.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: tobi</title>
		<link>http://barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39221</link>
		<dc:creator>tobi</dc:creator>
		<pubDate>Sat, 24 Nov 2007 04:15:50 +0000</pubDate>
		<guid isPermaLink="false">http://pezra.barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39221</guid>
		<description>&lt;p&gt;Your article is wrong, rails supports this since 1.0.&lt;/p&gt;

&lt;p&gt;Add a column called lock_version and Rails will increment this with every save.&lt;/p&gt;

&lt;p&gt;It will automatically implement optimistic locking for you and will raise an exception when you are trying to save stale data.&lt;/p&gt;

&lt;p&gt;Rails also has row level locking build in. When you find() an row you can pass :lock =&#62; true and it will a select for update.&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>Your article is wrong, rails supports this since 1.0.</p>
<p>Add a column called lock_version and Rails will increment this with every save.</p>
<p>It will automatically implement optimistic locking for you and will raise an exception when you are trying to save stale data.</p>
<p>Rails also has row level locking build in. When you find() an row you can pass :lock =&gt; true and it will a select for update.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: James Bennett</title>
		<link>http://barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39211</link>
		<dc:creator>James Bennett</dc:creator>
		<pubDate>Sat, 24 Nov 2007 02:31:16 +0000</pubDate>
		<guid isPermaLink="false">http://pezra.barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39211</guid>
		<description>&lt;p&gt;Peter, it's been ages since I've done anything with Rails (back in the pre-1.0 days, actually), but I'd be surprised if there wasn't a way to end a "SET TRANSACTION SERIALIZABLE" up-front.&lt;/p&gt;

&lt;p&gt;I don't know what the exact Ruby idiom would be, but in the Python DB adapters doing this means you'll get an exception raised from the DB if it detects someone else working on the same data, and you just catch that, abort and try again.&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>Peter, it&#8217;s been ages since I&#8217;ve done anything with Rails (back in the pre-1.0 days, actually), but I&#8217;d be surprised if there wasn&#8217;t a way to end a &#8220;SET TRANSACTION SERIALIZABLE&#8221; up-front.</p>
<p>I don&#8217;t know what the exact Ruby idiom would be, but in the Python DB adapters doing this means you&#8217;ll get an exception raised from the DB if it detects someone else working on the same data, and you just catch that, abort and try again.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Peter Williams</title>
		<link>http://barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39175</link>
		<dc:creator>Peter Williams</dc:creator>
		<pubDate>Fri, 23 Nov 2007 19:23:47 +0000</pubDate>
		<guid isPermaLink="false">http://pezra.barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39175</guid>
		<description>&lt;p&gt;Stephen,&lt;/p&gt;

&lt;p&gt;I definitely lean toward putting at least some constraints in the db.  It gives you a level of certainty about the data cleanliness that just cannot be achieved in the application code.   I have a post on this topic matriculating so hopefully I will just wait for it to talk more about this.&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>Stephen,</p>
<p>I definitely lean toward putting at least some constraints in the db.  It gives you a level of certainty about the data cleanliness that just cannot be achieved in the application code.   I have a post on this topic matriculating so hopefully I will just wait for it to talk more about this.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Peter Williams</title>
		<link>http://barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39174</link>
		<dc:creator>Peter Williams</dc:creator>
		<pubDate>Fri, 23 Nov 2007 19:21:08 +0000</pubDate>
		<guid isPermaLink="false">http://pezra.barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39174</guid>
		<description>&lt;p&gt;James,&lt;/p&gt;

&lt;p&gt;That is an interesting idea.  To be perfectly honest it had not occurred to me to change the isolation level just for the requests that needed it.  In rails this might be a little difficult because normally the transaction is already started before the action code gets invoked.  But I will have to look into it and see if it is possible.&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>James,</p>
<p>That is an interesting idea.  To be perfectly honest it had not occurred to me to change the isolation level just for the requests that needed it.  In rails this might be a little difficult because normally the transaction is already started before the action code gets invoked.  But I will have to look into it and see if it is possible.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Peter Williams</title>
		<link>http://barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39173</link>
		<dc:creator>Peter Williams</dc:creator>
		<pubDate>Fri, 23 Nov 2007 19:18:24 +0000</pubDate>
		<guid isPermaLink="false">http://pezra.barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39173</guid>
		<description>&lt;p&gt;Jay,&lt;/p&gt;

&lt;p&gt;It may be true that very few people have said something a direct as 'don't use database constraints' it is definately the feeling I get from the rails community.  And there is certainly no real support for DB constraints in AR.   You cannot even define database constraints without resorting to executing straight SQL in you migrations.&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>Jay,</p>
<p>It may be true that very few people have said something a direct as &#8216;don&#8217;t use database constraints&#8217; it is definately the feeling I get from the rails community.  And there is certainly no real support for DB constraints in AR.   You cannot even define database constraints without resorting to executing straight SQL in you migrations.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: jay</title>
		<link>http://barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39154</link>
		<dc:creator>jay</dc:creator>
		<pubDate>Fri, 23 Nov 2007 17:56:27 +0000</pubDate>
		<guid isPermaLink="false">http://pezra.barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39154</guid>
		<description>&lt;p&gt;"I am a fan of Ruby on Rails, but this whole ‘no constraints in the database’ is just plain wrong."&lt;/p&gt;

&lt;p&gt;I don't think anyone of any significance has said this. Please correct me if I'm wrong. What I have heard, and have said myself, is that I prefer my business logic in my app instead of in the db. I.e. I prefer ruby code to stored procedures. Every serious rails app I've worked on, and know about through friends, have db constraints.&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>&#8220;I am a fan of Ruby on Rails, but this whole ‘no constraints in the database’ is just plain wrong.&#8221;</p>
<p>I don&#8217;t think anyone of any significance has said this. Please correct me if I&#8217;m wrong. What I have heard, and have said myself, is that I prefer my business logic in my app instead of in the db. I.e. I prefer ruby code to stored procedures. Every serious rails app I&#8217;ve worked on, and know about through friends, have db constraints.</p>
]]></content:encoded>
	</item>
	<item>
		<title>By: Stephen</title>
		<link>http://barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39146</link>
		<dc:creator>Stephen</dc:creator>
		<pubDate>Fri, 23 Nov 2007 14:49:32 +0000</pubDate>
		<guid isPermaLink="false">http://pezra.barelyenough.org/blog/2007/11/activerecord-race-conditions/#comment-39146</guid>
		<description>&lt;p&gt;I am a fan of Ruby on Rails, but this whole 'no constraints in the database' is just plain wrong.&lt;/p&gt;

&lt;p&gt;I am a database programmer by trade and the ONLY way to ensure data integrity is to use database constraints.  In your solution above, you still may have a problem if you use Rails transactions:&lt;/p&gt;

&lt;p&gt;session 1: find record
session 2: find record
(both don't find a record)
session 1: create record, but no commit
session 2: create record, but no commit
(now both records are in the database)
...
...
session 1: commit some time later
session 2: commit some time later
session 2: will fail on commit if there is a unique constraint preventing the data, but not when your inserted the record.&lt;/p&gt;

&lt;p&gt;At least the constraint stops the bad data getting in, but the error raised may be at a different line of Ruby code than you expect.&lt;/p&gt;

&lt;p&gt;The Rails validations (especially the ensure_uniqueness_of) in my mind are purely for nice error messages 99% of the time.  As well as race conditions like you discovered, they are inefficient - if you have a heavily inserted table, then Rails has to select to ensure uniqueness, and then insert - if you just put a unique constrain on the table and do the insert, you save on the select.  This would only ever be an issue in a very heavily inserted table that has a unique column, which is probably rare!&lt;/p&gt;
</description>
		<content:encoded><![CDATA[<p>I am a fan of Ruby on Rails, but this whole &#8216;no constraints in the database&#8217; is just plain wrong.</p>
<p>I am a database programmer by trade and the ONLY way to ensure data integrity is to use database constraints.  In your solution above, you still may have a problem if you use Rails transactions:</p>
<p>session 1: find record<br />
session 2: find record<br />
(both don&#8217;t find a record)<br />
session 1: create record, but no commit<br />
session 2: create record, but no commit<br />
(now both records are in the database)<br />
&#8230;<br />
&#8230;<br />
session 1: commit some time later<br />
session 2: commit some time later<br />
session 2: will fail on commit if there is a unique constraint preventing the data, but not when your inserted the record.</p>
<p>At least the constraint stops the bad data getting in, but the error raised may be at a different line of Ruby code than you expect.</p>
<p>The Rails validations (especially the ensure_uniqueness_of) in my mind are purely for nice error messages 99% of the time.  As well as race conditions like you discovered, they are inefficient - if you have a heavily inserted table, then Rails has to select to ensure uniqueness, and then insert - if you just put a unique constrain on the table and do the insert, you save on the select.  This would only ever be an issue in a very heavily inserted table that has a unique column, which is probably rare!</p>
]]></content:encoded>
	</item>
</channel>
</rss>
