fix deploy problems due to renamed table
This commit is contained in:
parent
ff6b9aee47
commit
8b9799507c
|
@ -0,0 +1,12 @@
|
||||||
|
# Delayed migration steps
|
||||||
|
|
||||||
|
require 'table_migration_helper'
|
||||||
|
|
||||||
|
TableMigrationHelper.delayed_drop(
|
||||||
|
old_name: 'topic_status_updates',
|
||||||
|
new_name: 'topic_timers',
|
||||||
|
after_migration: 'RenameTopicStatusUpdatesToTopicTimers',
|
||||||
|
on_drop: ->(){
|
||||||
|
STDERR.puts "Dropping topic_status_updates. It was moved to topic_timers."
|
||||||
|
}
|
||||||
|
)
|
|
@ -0,0 +1,23 @@
|
||||||
|
require 'table_migration_helper'
|
||||||
|
|
||||||
|
class CreateTopicStatusUpdatesAgain < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
create_table :topic_status_updates do |t|
|
||||||
|
t.datetime :execute_at, null: false
|
||||||
|
t.integer :status_type, null: false
|
||||||
|
t.integer :user_id, null: false
|
||||||
|
t.integer :topic_id, null: false
|
||||||
|
t.boolean :based_on_last_post, null: false, default: false
|
||||||
|
t.datetime :deleted_at
|
||||||
|
t.integer :deleted_by_id
|
||||||
|
t.timestamps
|
||||||
|
t.integer :category_id
|
||||||
|
end
|
||||||
|
|
||||||
|
TableMigrationHelper.read_only_table('topic_status_updates')
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
drop_table :topic_status_updates
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,53 @@
|
||||||
|
class TableMigrationHelper
|
||||||
|
def self.read_only_table(table_name)
|
||||||
|
ActiveRecord::Base.exec_sql <<-SQL
|
||||||
|
CREATE OR REPLACE FUNCTION raise_read_only() RETURNS trigger AS $rro$
|
||||||
|
BEGIN
|
||||||
|
RAISE EXCEPTION 'Table is read only';
|
||||||
|
RETURN null;
|
||||||
|
END
|
||||||
|
$rro$ LANGUAGE plpgsql;
|
||||||
|
SQL
|
||||||
|
|
||||||
|
ActiveRecord::Base.exec_sql <<-SQL
|
||||||
|
CREATE TRIGGER #{table_name}_read_only
|
||||||
|
BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE
|
||||||
|
ON #{table_name}
|
||||||
|
FOR EACH STATEMENT
|
||||||
|
EXECUTE PROCEDURE raise_read_only();
|
||||||
|
SQL
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.delayed_drop(old_name:, new_name:, after_migration:, delay: nil, on_drop: nil)
|
||||||
|
delay ||= Rails.env.production? ? 300 : 0
|
||||||
|
|
||||||
|
sql = <<SQL
|
||||||
|
SELECT 1
|
||||||
|
FROM INFORMATION_SCHEMA.TABLES
|
||||||
|
WHERE table_schema = 'public' AND
|
||||||
|
table_name = :old_name AND
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM schema_migration_details
|
||||||
|
WHERE name = :after_migration AND
|
||||||
|
created_at <= (current_timestamp at time zone 'UTC' - interval :delay)
|
||||||
|
)
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM INFORMATION_SCHEMA.TABLES
|
||||||
|
WHERE table_schema = 'public' AND
|
||||||
|
table_name = :new_name
|
||||||
|
)
|
||||||
|
LIMIT 1
|
||||||
|
SQL
|
||||||
|
|
||||||
|
if ActiveRecord::Base.exec_sql(sql, old_name: old_name,
|
||||||
|
new_name: new_name,
|
||||||
|
delay: "#{delay.to_i || 0} seconds",
|
||||||
|
after_migration: after_migration).to_a.length > 0
|
||||||
|
on_drop&.call
|
||||||
|
|
||||||
|
ActiveRecord::Base.exec_sql("DROP TABLE #{old_name}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,66 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
require 'table_migration_helper'
|
||||||
|
|
||||||
|
describe TableMigrationHelper do
|
||||||
|
|
||||||
|
def table_exists?(table_name)
|
||||||
|
sql = <<-SQL
|
||||||
|
SELECT 1
|
||||||
|
FROM INFORMATION_SCHEMA.TABLES
|
||||||
|
WHERE table_schema = 'public' AND
|
||||||
|
table_name = '#{table_name}'
|
||||||
|
SQL
|
||||||
|
|
||||||
|
ActiveRecord::Base.exec_sql(sql).to_a.length > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#delayed_drop' do
|
||||||
|
it "can drop a table after correct delay and when new table exists" do
|
||||||
|
ActiveRecord::Base.exec_sql "CREATE TABLE table_with_old_name (topic_id INTEGER)"
|
||||||
|
|
||||||
|
name = ActiveRecord::Base
|
||||||
|
.exec_sql("SELECT name FROM schema_migration_details LIMIT 1")
|
||||||
|
.getvalue(0,0)
|
||||||
|
|
||||||
|
Topic.exec_sql("UPDATE schema_migration_details SET created_at = :created_at WHERE name = :name",
|
||||||
|
name: name, created_at: 15.minutes.ago)
|
||||||
|
|
||||||
|
dropped_proc_called = false
|
||||||
|
|
||||||
|
described_class.delayed_drop(
|
||||||
|
old_name: 'table_with_old_name',
|
||||||
|
new_name: 'table_with_new_name',
|
||||||
|
after_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(
|
||||||
|
old_name: 'table_with_old_name',
|
||||||
|
new_name: 'table_with_new_name',
|
||||||
|
after_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)
|
||||||
|
|
||||||
|
ActiveRecord::Base.exec_sql "CREATE TABLE table_with_new_name (topic_id INTEGER)"
|
||||||
|
|
||||||
|
described_class.delayed_drop(
|
||||||
|
old_name: 'table_with_old_name',
|
||||||
|
new_name: 'table_with_new_name',
|
||||||
|
after_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
|
Loading…
Reference in New Issue