Ignore delay when first migration was < 10min ago

This commit is contained in:
Gerhard Schlager 2018-03-26 16:51:27 +02:00 committed by Sam
parent cd17f60952
commit 4ad401bac5
3 changed files with 226 additions and 88 deletions

View File

@ -33,7 +33,12 @@ module Migration
SELECT 1
FROM schema_migration_details
WHERE name = :after_migration AND
created_at <= (current_timestamp AT TIME ZONE 'UTC' - INTERVAL :delay)
(created_at <= (current_timestamp AT TIME ZONE 'UTC' - INTERVAL :delay) OR
(SELECT created_at
FROM schema_migration_details
ORDER BY id ASC
LIMIT 1) > (current_timestamp AT TIME ZONE 'UTC' - INTERVAL '10 minutes')
)
)
SQL
end

View File

@ -4,49 +4,97 @@ require_dependency 'migration/column_dropper'
RSpec.describe Migration::ColumnDropper do
def has_column?(table, column)
Topic.exec_sql("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE
table_schema = 'public' AND
table_name = :table AND
column_name = :column
",
table: table, column: column
).to_a.length == 1
ActiveRecord::Base.exec_sql(<<~SQL, table: table, column: column).to_a.length == 1
SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE
table_schema = 'public' AND
table_name = :table AND
column_name = :column
SQL
end
it "can correctly drop columns after correct delay" do
Topic.exec_sql "ALTER TABLE topics ADD COLUMN junk int"
name = Topic
.exec_sql("SELECT name FROM schema_migration_details LIMIT 1")
.getvalue(0, 0)
def update_first_migration_date(created_at)
ActiveRecord::Base.exec_sql(<<~SQL, created_at: created_at)
UPDATE schema_migration_details
SET created_at = :created_at
WHERE id = (SELECT MIN(id)
FROM schema_migration_details)
SQL
end
Topic.exec_sql("UPDATE schema_migration_details SET created_at = :created_at WHERE name = :name",
name: name, created_at: 15.minutes.ago)
describe ".drop" do
let(:migration_name) do
ActiveRecord::Base
.exec_sql("SELECT name FROM schema_migration_details ORDER BY id DESC LIMIT 1")
.getvalue(0, 0)
end
dropped_proc_called = false
before do
Topic.exec_sql "ALTER TABLE topics ADD COLUMN junk int"
Migration::ColumnDropper.drop(
table: 'topics',
after_migration: name,
columns: ['junk'],
delay: 20.minutes,
on_drop: ->() { dropped_proc_called = true }
)
ActiveRecord::Base.exec_sql(<<~SQL, name: migration_name, created_at: 15.minutes.ago)
UPDATE schema_migration_details
SET created_at = :created_at
WHERE name = :name
SQL
end
expect(has_column?('topics', 'junk')).to eq(true)
expect(dropped_proc_called).to eq(false)
it "can correctly drop columns after correct delay" do
dropped_proc_called = false
update_first_migration_date(2.years.ago)
Migration::ColumnDropper.drop(
table: 'topics',
after_migration: name,
columns: ['junk'],
delay: 10.minutes,
on_drop: ->() { dropped_proc_called = true }
)
Migration::ColumnDropper.drop(
table: 'topics',
after_migration: migration_name,
columns: ['junk'],
delay: 20.minutes,
on_drop: ->() { dropped_proc_called = true }
)
expect(has_column?('topics', 'junk')).to eq(false)
expect(dropped_proc_called).to eq(true)
expect(has_column?('topics', 'junk')).to eq(true)
expect(dropped_proc_called).to eq(false)
Migration::ColumnDropper.drop(
table: 'topics',
after_migration: migration_name,
columns: ['junk'],
delay: 10.minutes,
on_drop: ->() { dropped_proc_called = true }
)
expect(has_column?('topics', 'junk')).to eq(false)
expect(dropped_proc_called).to eq(true)
end
it "drops the columns immediately if the first migration was less than 10 minutes ago" do
dropped_proc_called = false
update_first_migration_date(11.minutes.ago)
Migration::ColumnDropper.drop(
table: 'topics',
after_migration: migration_name,
columns: ['junk'],
delay: 30.minutes,
on_drop: ->() { dropped_proc_called = true }
)
expect(has_column?('topics', 'junk')).to eq(true)
expect(dropped_proc_called).to eq(false)
update_first_migration_date(9.minutes.ago)
Migration::ColumnDropper.drop(
table: 'topics',
after_migration: migration_name,
columns: ['junk'],
delay: 30.minutes,
on_drop: ->() { dropped_proc_called = true }
)
expect(has_column?('topics', 'junk')).to eq(false)
expect(dropped_proc_called).to eq(true)
end
end
describe '.mark_readonly' do

View File

@ -14,83 +14,168 @@ describe Migration::TableDropper do
ActiveRecord::Base.exec_sql(sql).to_a.length > 0
end
def update_first_migration_date(created_at)
ActiveRecord::Base.exec_sql(<<~SQL, created_at: created_at)
UPDATE schema_migration_details
SET created_at = :created_at
WHERE id = (SELECT MIN(id)
FROM schema_migration_details)
SQL
end
def create_new_table
ActiveRecord::Base.exec_sql "CREATE TABLE table_with_new_name (topic_id INTEGER)"
end
let(:migration_name) do
ActiveRecord::Base
.exec_sql("SELECT name FROM schema_migration_details LIMIT 1")
.exec_sql("SELECT name FROM schema_migration_details ORDER BY id DESC LIMIT 1")
.getvalue(0, 0)
end
before do
ActiveRecord::Base.exec_sql "CREATE TABLE table_with_old_name (topic_id INTEGER)"
Topic.exec_sql("UPDATE schema_migration_details SET created_at = :created_at WHERE name = :name",
name: migration_name, created_at: 15.minutes.ago)
ActiveRecord::Base.exec_sql(<<~SQL, name: migration_name, created_at: 15.minutes.ago)
UPDATE schema_migration_details
SET created_at = :created_at
WHERE name = :name
SQL
end
describe "#delayed_rename" do
it "can drop a table after correct delay and when new table exists" do
dropped_proc_called = false
context "first migration was a long time ago" do
before do
update_first_migration_date(2.years.ago)
end
described_class.delayed_rename(
old_name: 'table_with_old_name',
new_name: 'table_with_new_name',
after_migration: migration_name,
delay: 20.minutes,
on_drop: ->() { dropped_proc_called = true }
)
describe ".delayed_rename" do
it "can drop a table after correct delay and when new table exists" do
dropped_proc_called = false
expect(table_exists?('table_with_old_name')).to eq(true)
expect(dropped_proc_called).to eq(false)
described_class.delayed_rename(
old_name: 'table_with_old_name',
new_name: 'table_with_new_name',
after_migration: migration_name,
delay: 20.minutes,
on_drop: ->() { dropped_proc_called = true }
)
described_class.delayed_rename(
old_name: 'table_with_old_name',
new_name: 'table_with_new_name',
after_migration: migration_name,
delay: 10.minutes,
on_drop: ->() { dropped_proc_called = true }
)
expect(table_exists?('table_with_old_name')).to eq(true)
expect(dropped_proc_called).to eq(false)
expect(table_exists?('table_with_old_name')).to eq(true)
expect(dropped_proc_called).to eq(false)
described_class.delayed_rename(
old_name: 'table_with_old_name',
new_name: 'table_with_new_name',
after_migration: migration_name,
delay: 10.minutes,
on_drop: ->() { dropped_proc_called = true }
)
ActiveRecord::Base.exec_sql "CREATE TABLE table_with_new_name (topic_id INTEGER)"
expect(table_exists?('table_with_old_name')).to eq(true)
expect(dropped_proc_called).to eq(false)
described_class.delayed_rename(
old_name: 'table_with_old_name',
new_name: 'table_with_new_name',
after_migration: migration_name,
delay: 10.minutes,
on_drop: ->() { dropped_proc_called = true }
)
create_new_table
expect(table_exists?('table_with_old_name')).to eq(false)
expect(dropped_proc_called).to eq(true)
described_class.delayed_rename(
old_name: 'table_with_old_name',
new_name: 'table_with_new_name',
after_migration: migration_name,
delay: 10.minutes,
on_drop: ->() { dropped_proc_called = true }
)
expect(table_exists?('table_with_old_name')).to eq(false)
expect(dropped_proc_called).to eq(true)
end
end
describe ".delayed_drop" do
it "can drop a table after correct delay" do
dropped_proc_called = false
described_class.delayed_drop(
table_name: 'table_with_old_name',
after_migration: migration_name,
delay: 20.minutes,
on_drop: ->() { dropped_proc_called = true }
)
expect(table_exists?('table_with_old_name')).to eq(true)
expect(dropped_proc_called).to eq(false)
described_class.delayed_drop(
table_name: 'table_with_old_name',
after_migration: migration_name,
delay: 10.minutes,
on_drop: ->() { dropped_proc_called = true }
)
expect(table_exists?('table_with_old_name')).to eq(false)
expect(dropped_proc_called).to eq(true)
end
end
end
describe "#delayed_drop" do
it "can drop a table after correct delay" do
dropped_proc_called = false
context "first migration was a less than 10 minutes ago" do
describe ".delayed_rename" do
it "can drop a table after correct delay and when new table exists" do
dropped_proc_called = false
update_first_migration_date(11.minutes.ago)
create_new_table
described_class.delayed_drop(
table_name: 'table_with_old_name',
after_migration: migration_name,
delay: 20.minutes,
on_drop: ->() { dropped_proc_called = true }
)
described_class.delayed_rename(
old_name: 'table_with_old_name',
new_name: 'table_with_new_name',
after_migration: migration_name,
delay: 30.minutes,
on_drop: ->() { dropped_proc_called = true }
)
expect(table_exists?('table_with_old_name')).to eq(true)
expect(dropped_proc_called).to eq(false)
expect(table_exists?('table_with_old_name')).to eq(true)
expect(dropped_proc_called).to eq(false)
described_class.delayed_drop(
table_name: 'table_with_old_name',
after_migration: migration_name,
delay: 10.minutes,
on_drop: ->() { dropped_proc_called = true }
)
update_first_migration_date(9.minutes.ago)
expect(table_exists?('table_with_old_name')).to eq(false)
expect(dropped_proc_called).to eq(true)
described_class.delayed_rename(
old_name: 'table_with_old_name',
new_name: 'table_with_new_name',
after_migration: migration_name,
delay: 30.minutes,
on_drop: ->() { dropped_proc_called = true }
)
expect(table_exists?('table_with_old_name')).to eq(false)
expect(dropped_proc_called).to eq(true)
end
end
describe ".delayed_drop" do
it "immediately drops the table" do
dropped_proc_called = false
update_first_migration_date(11.minutes.ago)
described_class.delayed_drop(
table_name: 'table_with_old_name',
after_migration: migration_name,
delay: 30.minutes,
on_drop: ->() { dropped_proc_called = true }
)
expect(table_exists?('table_with_old_name')).to eq(true)
expect(dropped_proc_called).to eq(false)
update_first_migration_date(9.minutes.ago)
described_class.delayed_drop(
table_name: 'table_with_old_name',
after_migration: migration_name,
delay: 30.minutes,
on_drop: ->() { dropped_proc_called = true }
)
expect(table_exists?('table_with_old_name')).to eq(false)
expect(dropped_proc_called).to eq(true)
end
end
end
end