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

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

Migration Overrun

Railsの便利タスク、db:drop, db:schema:load, db:migrate, db:migrate:reset...
先日も共有の開発データベースが消えかけて、開発チームが半日開店休業なんてことがありました。

開発環境だから、「軽微事故」での笑い話。月１くらいで、身近に見かけます。
統計的[ヒヤリハット](http://tocana.jp/2014/08/post_4668_entry.html)で考えると、数年内に「重大事故」になりかねません。

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

- バックアップからのリストア （過去時点へのデータ戻し）
- アクセスログ・Mysql-binlogからの戻し　（人力）
- お客様への謝罪告知　と　謝罪行脚
- 再発防止のための運用変更
- お客様の信頼を失って業務終了…

実際、世界中の記事で、ちょいちょい見かけますものね。
事業継続性を揺るがす大惨事を。

というわけで、実装してみました。
意外とドキュメント見つけにくいんですよねー


# DB:migrateを撲滅する（憎しみをこめて）

実は簡単。
Railsだったら、以下6行。


```ruby: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')
```
などのように除外設定も可能。


```shell-session
$ 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
...
```
見事に消えてくれましたｗ

以下のような方々におすすめですね
- APIのようなサブシステム
- 運用中だがmigrationを金輪際実行しないアプリケーション


# とはいえ、やっぱ要るよね、migration

一時の憎しみと恐怖に駆られてみたものの、
運用回らなくなっちゃいますよね。

- テストが動かない
- 開発環境の新規構築ができない

とかとかで。

そんなあなたには、「productionでは実行させない！」例をば。

```ruby: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モードで見てみると、こんな感じ

```shell-session
$ 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](http://stackoverflow.com/questions/24785428/how-do-i-disable-the-migrations-feature-in-a-rails-app/26107802#26107802) migration撲滅談義
[Disable dangerous rake tasks in production](http://www.developingandrails.com/2015/01/disable-dangerous-rake-tasks-in.html) プロダクション環境でやらかして…涙なしには読めません
[Qiita Railsのマイグレーションをフックする](http://qiita.com/fakestarbaby/items/b505bf4fc4103c998cf2)　シンプルな技術記事。こういう時に使うんですねー