ありますよね、こんなこと。
Migration Overrun
Railsの便利タスク、db:drop, db:schema:load, db:migrate, db:migrate:reset...
先日も共有の開発データベースが消えかけて、開発チームが半日開店休業なんてことがありました。
開発環境だから、「軽微事故」での笑い話。月1くらいで、身近に見かけます。
統計的ヒヤリハットで考えると、数年内に「重大事故」になりかねません。
これが「もし本番だったら…」
- バックアップからのリストア (過去時点へのデータ戻し)
- アクセスログ・Mysql-binlogからの戻し (人力)
- お客様への謝罪告知 と 謝罪行脚
- 再発防止のための運用変更
- お客様の信頼を失って業務終了…
実際、世界中の記事で、ちょいちょい見かけますものね。
事業継続性を揺るがす大惨事を。
というわけで、実装してみました。
意外とドキュメント見つけにくいんですよねー
DB:migrateを撲滅する(憎しみをこめて)
実は簡単。
Railsだったら、以下6行。
Rake::Task.tasks.each do |t|
if t.name[0,3] == "db:"
t.clear
t.add_description("!!! Disabled in favor of enterprise design at Acme.")
end
end
タスク自体を不活化します。
今回は、db:系rakeタスクをgenocideしちゃいました。
db:migrate以下だけ、db:seedだけ などもできますね。
if t.name[0,3] == "db:" && !t.name.start_with?('db:structure:dump')
などのように除外設定も可能。
$ bundle exec rake -T
...
rake cache_digests:dependencies # Lookup first-level dependencies for TEMPLATE (like messages/show or comments/_comment.html)
rake cache_digests:nested_dependencies # Lookup nested dependencies for TEMPLATE (like messages/show or comments/_comment.html)
rake db:_dump # !!! Disabled in favor of enterprise design at Acme
rake db:abort_if_pending_migrations # !!! Disabled in favor of enterprise design at Acme
rake db:charset # !!! Disabled in favor of enterprise design at Acme
rake db:collation # !!! Disabled in favor of enterprise design at Acme
rake db:create # !!! Disabled in favor of enterprise design at Acme
rake db:create:all # !!! Disabled in favor of enterprise design at Acme
rake db:drop # !!! Disabled in favor of enterprise design at Acme
rake db:drop:all # !!! Disabled in favor of enterprise design at Acme
...
見事に消えてくれましたw
以下のような方々におすすめですね
- APIのようなサブシステム
- 運用中だがmigrationを金輪際実行しないアプリケーション
とはいえ、やっぱ要るよね、migration
一時の憎しみと恐怖に駆られてみたものの、
運用回らなくなっちゃいますよね。
- テストが動かない
- 開発環境の新規構築ができない
とかとかで。
そんなあなたには、「productionでは実行させない!」例をば。
namespace :guard do
desc 'Disable a task in production/development environments'
task :db_by_env do
if Rails.env.production?
puts 'This task is disabled. Only available in test environments.'
exit
end
end
end
def guard?(task)
return false if task.name.in? ['db:structure:dump', 'db:version', 'db:load_config']
task.name.start_with? 'db:'
end
Rake::Task.tasks.each do |t|
Rake::Task[t].enhance ['guard:db_by_env'] if guard? t
end
ガード用のタスクを作り、タスク実行前に挟む、というシロモノ。
実行処理をtraceモードで見てみると、こんな感じ
$ rake db:version RAILS_ENV=production --trace
** Invoke db:version (first_time)
** Invoke environment (first_time)
** Execute environment
** Invoke db:load_config (first_time)
** Invoke guard:db_by_env (first_time)
** Execute guard:db_by_env
This task is disabled. Only available in test environments.
ちゃんと処理実行前に落ちてくれてますね。
これで皆さん、明日から枕高く寝られますねー
参考文献
Stackoverflow migration撲滅談義
Disable dangerous rake tasks in production プロダクション環境でやらかして…涙なしには読めません
Qiita Railsのマイグレーションをフックする シンプルな技術記事。こういう時に使うんですねー