[LRUG] Enforcing uniqueness

Andrew Stewart boss at airbladesoftware.com
Fri Nov 11 06:22:54 PST 2011


Hola El Rug,

Let's say I am modelling book libraries in ActiveRecord.  A library has many books and a book has many loans.  A loan is "current" while the book is out of the library; and, er, not current when the book is (back) in the library.

    class Library < ActiveRecord::Base
      has_many :books
    end

    class Book < ActiveRecord::Base
      belongs_to :library
      has_many :loans
      has_one  :current_loan, :class_name => 'Loan', :conditions => 'loans.returned_at is null'
    end

    class Loan < ActiveRecord::Base
      belongs_to :book
    end

I cannot figure out how to ensure a book can't end up with two current loans in the database.  I am seeing this at the moment due to, I believe, double-click form submissions in the GUI.

Rails' uniqueness validator is vulnerable to race conditions, as my database attests.  The usual answer is to apply a unique index in the database.  But I want an index on the loans table like "unique(book_id) where returned_at is null" -- which isn't possible, as far as I know.

I think there are four layers where this could be tackled:

View: add client-side behaviour to prevent duplicate form submissions.
=> Mitigates the problem but doesn't really solve it.

Controller: serialise access to the action where loans are created, perhaps per library (to reduce contention).
=> Mitigates the problem but doesn't really solve it.  Also I'm not sure how to implement.

Model: use a validator
=> This is what I'm already doing.  It's not bullet proof.

Database: use an index
=> I don't think it's possible given my data model.

Given all of the above, I think the controller is my best bet.  But I'm not sure how to do it.

Any ideas?

Cheers,
Andy Stewart

-------
http://airbladesoftware.com


More information about the Chat mailing list