Active Record Scoped Sequence
activerecord-scoped_sequence assigns increasing integer values within the
scope of a belongs_to association. It serializes allocation by locking the
associated row, so concurrent inserts for one scope cannot receive the same
generated value.
Installation
Add the gem to your application:
gem "activerecord-scoped_sequence"
Then run bundle install.
Usage
Add the sequence column and an index covering its scope:
add_column :invoices, :number, :bigint
add_index :invoices, [:organization_id, :number], unique: true
Declare the sequence on the model:
class Invoice < ApplicationRecord
belongs_to :organization
scoped_sequence :number, scope: :organization
end
The organization is locked while the next number is assigned by querying the maximum invoice number currently persisted for it and adding one. An explicitly supplied number is preserved.
Concurrency and indexes
The lock is acquired in before_create, inside Active Record's implicit save
transaction. Database row locks live until that transaction commits or rolls
back, so the lock remains held through the insert even after the callback
returns.
Every writer must use callbacks for allocation. Bulk APIs such as insert_all
bypass the macro. A composite unique index remains strongly recommended as a
database invariant; it is defensive and is not used as a retry mechanism.
The gem currently supports non-polymorphic belongs_to associations with
single-column keys.