From ec8ba5a0b9ff8dbefae74d6ec4f08dc13e418c6b Mon Sep 17 00:00:00 2001 From: Bianca Nenciu Date: Tue, 27 Aug 2024 05:56:00 +0300 Subject: [PATCH] DEV: Migrate shelved_notifications#notification_id to bigint (#28549) DEV: Migrate shelved_notifications#notification_id to bigint The `notifications.id` has been migrated to `bigint` in previous commit 799a45a291e9f2bd94278f565e58874458768079. --- app/models/notification.rb | 4 +++ app/models/shelved_notification.rb | 9 ++++-- ...t_shelved_notifications_notification_id.rb | 30 +++++++++++++++++ ...ed_notifications_notification_id_values.rb | 20 ++++++++++++ ...d_notifications_notification_id_indexes.rb | 14 ++++++++ ...t_shelved_notifications_notification_id.rb | 32 +++++++++++++++++++ 6 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20240826121501_add_big_int_shelved_notifications_notification_id.rb create mode 100644 db/migrate/20240826121502_copy_shelved_notifications_notification_id_values.rb create mode 100644 db/migrate/20240826121503_copy_shelved_notifications_notification_id_indexes.rb create mode 100644 db/migrate/20240826121504_swap_big_int_shelved_notifications_notification_id.rb diff --git a/app/models/notification.rb b/app/models/notification.rb index 814cc3a6f3d..c165aacdbc5 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true class Notification < ActiveRecord::Base + self.ignored_columns = [ + :old_id, # TODO: Remove when column is dropped. At this point, the migration to drop the column has not been writted. + ] + attr_accessor :acting_user attr_accessor :acting_username diff --git a/app/models/shelved_notification.rb b/app/models/shelved_notification.rb index e0eb9ebd56d..56ab53a7d0b 100644 --- a/app/models/shelved_notification.rb +++ b/app/models/shelved_notification.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true class ShelvedNotification < ActiveRecord::Base + self.ignored_columns = [ + :old_notification_id, # TODO: Remove when column is dropped. At this point, the migration to drop the column has not been writted. + ] + belongs_to :notification def process @@ -12,8 +16,9 @@ end # # Table name: shelved_notifications # -# id :bigint not null, primary key -# notification_id :integer not null +# id :bigint not null, primary key +# old_notification_id :integer +# notification_id :bigint not null # # Indexes # diff --git a/db/migrate/20240826121501_add_big_int_shelved_notifications_notification_id.rb b/db/migrate/20240826121501_add_big_int_shelved_notifications_notification_id.rb new file mode 100644 index 00000000000..4fbe7f19c65 --- /dev/null +++ b/db/migrate/20240826121501_add_big_int_shelved_notifications_notification_id.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class AddBigIntShelvedNotificationsNotificationId < ActiveRecord::Migration[7.0] + def up + # Create new column + execute "ALTER TABLE shelved_notifications ADD COLUMN new_notification_id BIGINT NOT NULL DEFAULT 0" + + # Mirror new `notification_id` values to `new_notification_id` + execute <<~SQL.squish + CREATE FUNCTION mirror_user_badges_notification_id() + RETURNS trigger AS + $$ + BEGIN + NEW.new_notification_id = NEW.notification_id; + RETURN NEW; + END; + $$ + LANGUAGE plpgsql + SQL + + execute <<~SQL.squish + CREATE TRIGGER user_badges_new_notification_id_trigger BEFORE INSERT ON shelved_notifications + FOR EACH ROW EXECUTE PROCEDURE mirror_user_badges_notification_id() + SQL + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/migrate/20240826121502_copy_shelved_notifications_notification_id_values.rb b/db/migrate/20240826121502_copy_shelved_notifications_notification_id_values.rb new file mode 100644 index 00000000000..28ec64fedd6 --- /dev/null +++ b/db/migrate/20240826121502_copy_shelved_notifications_notification_id_values.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class CopyShelvedNotificationsNotificationIdValues < ActiveRecord::Migration[7.0] + disable_ddl_transaction! + + def up + min_id, max_id = execute("SELECT MIN(id), MAX(id) FROM shelved_notifications")[0].values + batch_size = 10_000 + + (min_id..max_id).step(batch_size) { |start_id| execute <<~SQL.squish } if min_id && max_id + UPDATE shelved_notifications + SET new_notification_id = notification_id + WHERE id >= #{start_id} AND id < #{start_id + batch_size} AND notification_id != new_notification_id + SQL + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/migrate/20240826121503_copy_shelved_notifications_notification_id_indexes.rb b/db/migrate/20240826121503_copy_shelved_notifications_notification_id_indexes.rb new file mode 100644 index 00000000000..9b025568b5d --- /dev/null +++ b/db/migrate/20240826121503_copy_shelved_notifications_notification_id_indexes.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class CopyShelvedNotificationsNotificationIdIndexes < ActiveRecord::Migration[7.0] + disable_ddl_transaction! + + def up + execute "DROP INDEX #{Rails.env.test? ? "" : "CONCURRENTLY"} IF EXISTS index_shelved_notifications_on_new_notification_id" + execute "CREATE INDEX #{Rails.env.test? ? "" : "CONCURRENTLY"} index_shelved_notifications_on_new_notification_id ON shelved_notifications (new_notification_id)" + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/migrate/20240826121504_swap_big_int_shelved_notifications_notification_id.rb b/db/migrate/20240826121504_swap_big_int_shelved_notifications_notification_id.rb new file mode 100644 index 00000000000..9dd953c7af8 --- /dev/null +++ b/db/migrate/20240826121504_swap_big_int_shelved_notifications_notification_id.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class SwapBigIntShelvedNotificationsNotificationId < ActiveRecord::Migration[7.0] + def up + # Necessary to rename columns and drop triggers/functions + Migration::SafeMigrate.disable! + + # Drop trigger and function used to replicate new values + execute "DROP TRIGGER user_badges_new_notification_id_trigger ON shelved_notifications" + execute "DROP FUNCTION mirror_user_badges_notification_id()" + + execute "ALTER TABLE shelved_notifications ALTER COLUMN new_notification_id DROP DEFAULT" + + # Swap columns + execute "ALTER TABLE shelved_notifications RENAME COLUMN notification_id TO old_notification_id" + execute "ALTER TABLE shelved_notifications RENAME COLUMN new_notification_id TO notification_id" + + # Keep old column and mark it as read only + execute "ALTER TABLE shelved_notifications ALTER COLUMN old_notification_id DROP NOT NULL" + Migration::ColumnDropper.mark_readonly(:shelved_notifications, :old_notification_id) + + # Rename indexes + execute "DROP INDEX IF EXISTS index_shelved_notifications_on_notification_id" + execute "ALTER INDEX index_shelved_notifications_on_new_notification_id RENAME TO index_shelved_notifications_on_notification_id" + ensure + Migration::SafeMigrate.enable! + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end