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.