Commit d374d0be authored by Andreas Brandl's avatar Andreas Brandl

Backwards-compat for migration specs.

The specs are based on a schema version that doesn't know about
`internal_ids` table. However, the actual code being execute relies on
it.
parent d4bb363f
...@@ -17,6 +17,8 @@ class InternalId < ActiveRecord::Base ...@@ -17,6 +17,8 @@ class InternalId < ActiveRecord::Base
validates :usage, presence: true validates :usage, presence: true
REQUIRED_SCHEMA_VERSION = 20180305095250
# Increments #last_value and saves the record # Increments #last_value and saves the record
# #
# The operation locks the record and gathers a `ROW SHARE` lock (in PostgreSQL). # The operation locks the record and gathers a `ROW SHARE` lock (in PostgreSQL).
...@@ -30,8 +32,22 @@ class InternalId < ActiveRecord::Base ...@@ -30,8 +32,22 @@ class InternalId < ActiveRecord::Base
class << self class << self
def generate_next(subject, scope, usage, init) def generate_next(subject, scope, usage, init)
# Shortcut if `internal_ids` table is not available (yet)
# This can be the case in other (unrelated) migration specs
return (init.call(subject) || 0) + 1 unless available?
InternalIdGenerator.new(subject, scope, usage, init).generate InternalIdGenerator.new(subject, scope, usage, init).generate
end end
def available?
@available_flag ||= ActiveRecord::Migrator.current_version >= REQUIRED_SCHEMA_VERSION # rubocop:disable Gitlab/PredicateMemoization
end
# Flushes cached information about schema
def reset_column_information
@available_flag = nil
super
end
end end
class InternalIdGenerator class InternalIdGenerator
...@@ -49,12 +65,12 @@ class InternalId < ActiveRecord::Base ...@@ -49,12 +65,12 @@ class InternalId < ActiveRecord::Base
# subject: The instance we're generating an internal id for. Gets passed to init if called. # subject: The instance we're generating an internal id for. Gets passed to init if called.
# scope: Attributes that define the scope for id generation. # scope: Attributes that define the scope for id generation.
# usage: Symbol to define the usage of the internal id, see InternalId.usages # usage: Symbol to define the usage of the internal id, see InternalId.usages
# init: Block that gets called to initialize InternalId record if not yet present (optional) # init: Block that gets called to initialize InternalId record if not present
attr_reader :subject, :scope, :init, :scope_attrs, :usage attr_reader :subject, :scope, :init, :scope_attrs, :usage
def initialize(subject, scope, usage, init) def initialize(subject, scope, usage, init)
@subject = subject @subject = subject
@scope = scope @scope = scope
@init = init || ->(s) { 0 } @init = init
@usage = usage @usage = usage
raise 'scope is not well-defined, need at least one column for scope (given: 0)' if scope.empty? raise 'scope is not well-defined, need at least one column for scope (given: 0)' if scope.empty?
......
...@@ -12,9 +12,9 @@ describe InternalId do ...@@ -12,9 +12,9 @@ describe InternalId do
end end
describe '.generate_next' do describe '.generate_next' do
context 'in the absence of a record' do subject { described_class.generate_next(issue, scope, usage, init) }
subject { described_class.generate_next(issue, scope, usage, init) }
context 'in the absence of a record' do
it 'creates a record if not yet present' do it 'creates a record if not yet present' do
expect { subject }.to change { described_class.count }.from(0).to(1) expect { subject }.to change { described_class.count }.from(0).to(1)
end end
...@@ -47,6 +47,21 @@ describe InternalId do ...@@ -47,6 +47,21 @@ describe InternalId do
normalized = seq.map { |i| i - seq.min } normalized = seq.map { |i| i - seq.min }
expect(normalized).to eq((0..seq.size - 1).to_a) expect(normalized).to eq((0..seq.size - 1).to_a)
end end
context 'with an insufficient schema version' do
before do
described_class.reset_column_information
expect(ActiveRecord::Migrator).to receive(:current_version).and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
end
let(:init) { double('block') }
it 'calculates next internal ids on the fly' do
val = rand(1..100)
expect(init).to receive(:call).with(issue).and_return(val)
expect(subject).to eq(val + 1)
end
end
end end
describe '#increment_and_save!' do describe '#increment_and_save!' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment