FEATURE: Post deployment migrations. (#6406)

This moves us away from the delayed drops pattern which
was problematic on two counts. First, it uses a hardcoded "delay for"
duration which may be too short for certain deployment strategies.
Second, delayed drop doesn't ensure that it only runs after
the latest application code has been deployed. If the migration runs
and the application code fails to deploy, running the migration after
"delay for" has been met will cause the application to blow up.

The new strategy allows post deployment migrations to be skipped if the
env `SKIP_POST_DEPLOYMENT_MIGRATIONS` is provided.

```
SKIP_POST_DEPLOYMENT_MIGRATIONS=1 rake db:migrate
-> deploy app servers
SKIP_POST_DEPLOYMENT_MIGRATIONS=0 rake db:migrate
```

To aid with the generation of a post deployment migration, a generator
has been added. Simply run `rails generate post_migration`.
This commit is contained in:
Guo Xiang Tan
2018-10-08 15:47:38 +08:00
committed by GitHub
parent 9bbc1ae7b2
commit 40fa96777d
26 changed files with 260 additions and 738 deletions

View File

@@ -1,226 +0,0 @@
# Delayed migration steps
require 'migration/table_dropper'
require 'migration/column_dropper'
require 'badge_posts_view_manager'
Migration::ColumnDropper.drop(
table: 'user_profiles',
after_migration: 'DropUserCardBadgeColumns',
columns: ['card_image_badge_id'],
on_drop: ->() {
STDERR.puts "Removing user_profiles column card_image_badge_id"
},
delay: 3600
)
Migration::ColumnDropper.drop(
table: 'categories',
after_migration: 'AddSuppressFromLatestToCategories',
columns: ['logo_url', 'background_url', 'suppress_from_homepage'],
on_drop: ->() {
STDERR.puts 'Removing superflous categories columns!'
}
)
Migration::ColumnDropper.drop(
table: 'groups',
after_migration: 'SplitAliasLevels',
columns: %w[visible public alias_level],
on_drop: ->() {
STDERR.puts 'Removing superflous visible group column!'
}
)
Migration::ColumnDropper.drop(
table: 'theme_fields',
after_migration: 'AddUploadIdToThemeFields',
columns: ['target'],
on_drop: ->() {
STDERR.puts 'Removing superflous theme_fields target column!'
}
)
Migration::ColumnDropper.drop(
table: 'user_stats',
after_migration: 'DropUnreadTrackingColumns',
columns: %w{
first_topic_unread_at
},
on_drop: ->() {
STDERR.puts "Removing superflous user stats columns!"
DB.exec "DROP FUNCTION IF EXISTS first_unread_topic_for(int)"
}
)
Migration::ColumnDropper.drop(
table: 'topics',
after_migration: 'DropVoteCountFromTopicsAndPosts',
columns: %w{
auto_close_at
auto_close_user_id
auto_close_started_at
auto_close_based_on_last_post
auto_close_hours
inappropriate_count
bookmark_count
off_topic_count
illegal_count
notify_user_count
last_unread_at
vote_count
},
on_drop: ->() {
STDERR.puts "Removing superflous topic columns!"
}
)
Migration::ColumnDropper.drop(
table: 'posts',
after_migration: 'DropVoteCountFromTopicsAndPosts',
columns: %w{
vote_count
},
on_drop: ->() {
STDERR.puts "Removing superflous post columns!"
BadgePostsViewManager.drop!
},
after_drop: -> () {
BadgePostsViewManager.create!
}
)
Migration::ColumnDropper.drop(
table: 'users',
after_migration: 'DropEmailFromUsers',
columns: %w[
email
email_always
mailing_list_mode
email_digests
email_direct
email_private_messages
external_links_in_new_tab
enable_quoting
dynamic_favicon
disable_jump_reply
edit_history_public
automatically_unpin_topics
digest_after_days
auto_track_topics_after_msecs
new_topic_duration_minutes
last_redirected_to_top_at
auth_token
auth_token_updated_at
],
on_drop: ->() {
STDERR.puts 'Removing superflous users columns!'
}
)
Migration::ColumnDropper.drop(
table: 'users',
after_migration: 'RenameBlockedSilence',
columns: %w[
blocked
],
on_drop: ->() {
STDERR.puts 'Removing user blocked column!'
}
)
Migration::ColumnDropper.drop(
table: 'users',
after_migration: 'AddSilencedTillToUsers',
columns: %w[
silenced
],
on_drop: ->() {
STDERR.puts 'Removing user silenced column!'
}
)
Migration::ColumnDropper.drop(
table: 'users',
after_migration: 'AddTrustLevelLocksToUsers',
columns: %w[
trust_level_locked
],
on_drop: ->() {
STDERR.puts 'Removing user trust_level_locked!'
}
)
Migration::ColumnDropper.drop(
table: 'user_auth_tokens',
after_migration: 'RemoveLegacyAuthToken',
columns: %w[
legacy
],
on_drop: ->() {
STDERR.puts 'Removing user_auth_token legacy column!'
}
)
Migration::TableDropper.delayed_rename(
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."
}
)
Migration::TableDropper.delayed_drop(
table_name: 'category_featured_users',
after_migration: 'DropUnusedTables',
on_drop: ->() {
STDERR.puts "Dropping category_featured_users. It isn't used anymore."
}
)
Migration::TableDropper.delayed_drop(
table_name: 'versions',
after_migration: 'DropUnusedTables',
on_drop: ->() {
STDERR.puts "Dropping versions. It isn't used anymore."
}
)
Migration::ColumnDropper.drop(
table: 'user_options',
after_migration: 'DropKeyColumnFromThemes',
columns: %w[
theme_key
],
on_drop: ->() {
STDERR.puts 'Removing theme_key column from user_options table!'
}
)
Migration::ColumnDropper.drop(
table: 'themes',
after_migration: 'DropKeyColumnFromThemes',
columns: %w[
key
],
on_drop: ->() {
STDERR.puts 'Removing key column from themes table!'
}
)
Migration::ColumnDropper.drop(
table: 'email_logs',
after_migration: 'DropReplyKeySkippedSkippedReasonFromEmailLogs',
columns: %w{
topic_id
reply_key
skipped
skipped_reason
},
on_drop: ->() {
STDERR.puts "Removing superflous email_logs columns!"
}
)
Discourse.reset_active_record_cache

View File

@@ -1,9 +0,0 @@
class AddUnreadTrackingColumns < ActiveRecord::Migration[4.2]
def up
# no op, no need to create all data, next migration will delete it
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View File

@@ -1,8 +0,0 @@
class DropUnreadTrackingColumns < ActiveRecord::Migration[4.2]
def up
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View File

@@ -1,9 +0,0 @@
class RemovePublicFromGroups < ActiveRecord::Migration[4.2]
def up
# Defer dropping of the columns until the new application code has been deployed.
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View File

@@ -1,9 +0,0 @@
class DropEmailFromUsers < ActiveRecord::Migration[5.1]
def up
# Defer dropping of the columns until the new application code has been deployed.
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View File

@@ -1,9 +0,0 @@
class DropUnusedTables < ActiveRecord::Migration[5.1]
def up
# Delayed drop of tables "category_featured_users" and "versions"
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View File

@@ -1,5 +0,0 @@
class RemoveLegacyAuthToken < ActiveRecord::Migration[5.1]
def change
# placeholder so we can drop column in 009_users.rb
end
end

View File

@@ -1,9 +0,0 @@
class DropVoteCountFromTopicsAndPosts < ActiveRecord::Migration[5.2]
def up
# Delayed drop
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View File

@@ -0,0 +1,82 @@
require 'migration/column_dropper'
require 'badge_posts_view_manager'
class RemoveSuperfluousColumns < ActiveRecord::Migration[5.2]
def up
{
user_profiles: %i{
card_image_badge_id
},
categories: %i{
logo_url
background_url
suppress_from_homepage
},
groups: %i{
visible
public
alias_level
},
theme_fields: %i{target},
user_stats: %i{first_topic_unread_at},
topics: %i{
auto_close_at
auto_close_user_id
auto_close_started_at
auto_close_based_on_last_post
auto_close_hours
inappropriate_count
bookmark_count
off_topic_count
illegal_count
notify_user_count
last_unread_at
vote_count
},
users: %i{
email
email_always
mailing_list_mode
email_digests
email_direct
email_private_messages
external_links_in_new_tab
enable_quoting
dynamic_favicon
disable_jump_reply
edit_history_public
automatically_unpin_topics
digest_after_days
auto_track_topics_after_msecs
new_topic_duration_minutes
last_redirected_to_top_at
auth_token
auth_token_updated_at
blocked
silenced
trust_level_locked
},
user_auth_tokens: %i{legacy},
user_options: %i{theme_key},
themes: %i{key},
email_logs: %i{
topic_id
reply_key
skipped
skipped_reason
},
}.each do |table, columns|
Migration::ColumnDropper.execute_drop(table, columns)
end
DB.exec "DROP FUNCTION IF EXISTS first_unread_topic_for(int)"
BadgePostsViewManager.drop!
Migration::ColumnDropper.execute_drop(:posts, %i{vote_count})
BadgePostsViewManager.create!
end
def down
raise ActiveRecord::IrreversibleMigration
end
end

View File

@@ -0,0 +1,17 @@
require 'migration/table_dropper'
class RemoveSuperfluousTables < ActiveRecord::Migration[5.2]
def up
%i{
category_featured_users
versions
topic_status_updates
}.each do |table|
Migration::TableDropper.execute_drop(table)
end
end
def down
raise ActiveRecord::IrreversibleMigration
end
end