Ruby
Rails
Rails4

「やべっ、DB消えた!」を10行で撲滅

More than 1 year has passed since last update.

ありますよね、こんなこと。

Migration Overrun

Railsの便利タスク、db:drop, db:schema:load, db:migrate, db:migrate:reset...

先日も共有の開発データベースが消えかけて、開発チームが半日開店休業なんてことがありました。

開発環境だから、「軽微事故」での笑い話。月1くらいで、身近に見かけます。

統計的ヒヤリハットで考えると、数年内に「重大事故」になりかねません。

これが「もし本番だったら…」


  • バックアップからのリストア (過去時点へのデータ戻し)

  • アクセスログ・Mysql-binlogからの戻し (人力)

  • お客様への謝罪告知 と 謝罪行脚

  • 再発防止のための運用変更

  • お客様の信頼を失って業務終了…

実際、世界中の記事で、ちょいちょい見かけますものね。

事業継続性を揺るがす大惨事を。

というわけで、実装してみました。

意外とドキュメント見つけにくいんですよねー


DB:migrateを撲滅する(憎しみをこめて)

実は簡単。

Railsだったら、以下6行。


lib/tasks/db.rake

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では実行させない!」例をば。


lib/tasks/guard_db.rake

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のマイグレーションをフックする シンプルな技術記事。こういう時に使うんですねー