# Enforce an application constraint

**URL:** <https://www.ruby-forum.com/t/enforce-an-application-constraint/168619>\
**Category:** Rails\
**Created:** [May 23, 2009, 12:22am UTC](https://www.ruby-forum.com/t/enforce-an-application-constraint/168619 "2009-05-23T00:22:27Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![coriordan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/b5ac83/32.png) [@coriordan](https://www.ruby-forum.com/u/coriordan)\
**Post date:** [May 23, 2009, 12:22am UTC](https://www.ruby-forum.com/t/enforce-an-application-constraint/168619/1 "2009-05-23T00:22:27Z")

</div>

Hi,

In my app. I have the following set of models

class Course \< ActiveRecord::Base  
has\_many :registrations  
has\_many :users, :through =\> :registrations  
end

class User \< ActiveRecord::Base  
has\_many :registrations  
has\_many :courses, :through =\> :registrations  
end

class Registration \< ActiveRecord::Base  
belongs\_to :user  
belongs\_to :course  
end

each course model has a ‘capacity’ attribute, which dictates how many  
places are available on that particular course. This is how I enforce  
the ‘capacity’ constraint when a registration is created:

class Course \< ActiveRecord::Base  
…  
def enrol(user)  
if registration\_count \>= capacity  
errors.add\_to\_base(“this course is now full”)  
return false  
end  
self.registrations.create(:user\_id =\> user.id)  
end

def registration\_count  
registrations.length  
end  
…  
end

My concern is that my code won’t guard against a race condition where  
2 or more users try to register for a course that is near it capacity  
limit. My first thought was to wrap the registration code in a  
transaction and rollback if the capacity of registrations on a course  
has been exceeded.

I’m not sure if this is a valid approach and I would love to hear from  
someone who could provide some adivice on how I might proceed.

thanks and regards,  
C.

---

<div class="post-metadata">

**Author:** ![coriordan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/b5ac83/32.png) [@coriordan](https://www.ruby-forum.com/u/coriordan)\
**Post date:** [May 23, 2009, 1:21am UTC](https://www.ruby-forum.com/t/enforce-an-application-constraint/168619/2 "2009-05-23T01:21:45Z")

</div>

Cathal O’Riordan wrote:  
[…]

> My concern is that my code won’t guard against a race condition where  
> 2 or more users try to register for a course that is near it capacity  
> limit. My first thought was to wrap the registration code in a  
> transaction and rollback if the capacity of registrations on a course  
> has been exceeded.  
> […]

Use check constraints in the DB and let _it_, not your app, worry about  
concurrency issues!

If your DB server can’t handle check constraints, get one that can.

## Best,

Marnen Laibow-Koser  
[http://www.marnen.org](http://www.marnen.org)  
removed\_email\_address@domain.invalid

---

<div class="post-metadata">

**Author:** ![coriordan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/b5ac83/32.png) [@coriordan](https://www.ruby-forum.com/u/coriordan)\
**Post date:** [May 23, 2009, 4:05pm UTC](https://www.ruby-forum.com/t/enforce-an-application-constraint/168619/3 "2009-05-23T16:05:35Z")

</div>

Don’t check constrainsts just ensure that data is a certain format?

On May 23, 12:21 am, Marnen Laibow-Koser \<rails-mailing-l…@andreas-

---

<div class="post-metadata">

**Author:** ![coriordan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/b5ac83/32.png) [@coriordan](https://www.ruby-forum.com/u/coriordan)\
**Post date:** [May 23, 2009, 12:58pm UTC](https://www.ruby-forum.com/t/enforce-an-application-constraint/168619/4 "2009-05-23T12:58:05Z")

</div>

Marnen Laibow-Koser wrote:

> Cathal O’Riordan wrote:  
> […]
> 
> > My concern is that my code won’t guard against a race condition where  
> > 2 or more users try to register for a course that is near it capacity  
> > limit. My first thought was to wrap the registration code in a  
> > transaction and rollback if the capacity of registrations on a course  
> > has been exceeded.  
> > […]
> 
> Use check constraints in the DB and let _it_, not your app, worry about  
> concurrency issues!

That’s one approach. Wrapping it up in a transaction with read isolation  
would work fine too. Alternatively (and better for concurrency), keep a  
counter cache on the registration\_count and use the default isolation  
level.

–  
Roderick van Domburg

---

<div class="post-metadata">

**Author:** ![coriordan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/b5ac83/32.png) [@coriordan](https://www.ruby-forum.com/u/coriordan)\
**Post date:** [May 23, 2009, 6:58pm UTC](https://www.ruby-forum.com/t/enforce-an-application-constraint/168619/5 "2009-05-23T18:58:21Z")

</div>

Cathal O’Riordan wrote:

> Don’t check constrainsts just ensure that data is a certain format?

No. They check anything you like – format, value, whatever. They  
actually work a lot like Rails validators.

## Best,

Marnen Laibow-Koser  
[http://www.marnen.org](http://www.marnen.org)  
removed\_email\_address@domain.invalid

---

<div class="post-metadata">

**Author:** ![coriordan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/b5ac83/32.png) [@coriordan](https://www.ruby-forum.com/u/coriordan)\
**Post date:** [May 23, 2009, 4:13pm UTC](https://www.ruby-forum.com/t/enforce-an-application-constraint/168619/6 "2009-05-23T16:13:11Z")

</div>

Hi Roderick,

Could you provide an example of how this would work?

regards,  
C.

On May 23, 11:58 am, Roderick van Domburg \<rails-mailing-l…@andreas-

---

<div class="post-metadata">

**Author:** ![coriordan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/b5ac83/32.png) [@coriordan](https://www.ruby-forum.com/u/coriordan)\
**Post date:** [May 23, 2009, 8:24pm UTC](https://www.ruby-forum.com/t/enforce-an-application-constraint/168619/7 "2009-05-23T20:24:34Z")

</div>

Cathal O’Riordan wrote:

> Could you provide an example of how this would work?

Take a look at :counter\_cache at  
[ActiveRecord::Associations::ClassMethods](http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html).

Wrap everything up in a transaction, fetch the registration count,  
administer the new registration and update the counter cache. Any  
concurrent registrations will be rolled back by the database – don’t  
forget to catch that exception and deal with it.

–  
Roderick van Domburg  
[http://www.nedforce.com](http://www.nedforce.com)
