本記事のサマリ
capistranoでrailsアプリをdeployする際に、自動的にseedを流すようにすると、deployの運用的には楽になるが、seedの実行時間が待たされるので、seedファイルが描き変わった時だけ動くように工夫するといいかもという話です。
対象読者
- capistranoをつかって、railsアプリをデプロイしており、seedの実行は手動でやっているような方
- seedの実行をcapに任せているが、毎回時間かかっているような方
動作確認バージョン
Capistrano Version: 3.11.2 (Rake Version: 10.3.2)
Rails 5.1.6
ruby 2.6.6p146 (2020-03-31 revision 67876) [x86_64-darwin18]
経緯
capistranoでデプロイする際に、seedの実行は変更があったときに、手で実行するのは、面倒です。
なので、そういったものは、自動的にやるようにしたいというのが、怠惰なプログラマな正常な欲求です。
でも、自動的にやってみたのはいいものの、毎回実行していると、seedの肥大化に伴って、実行時間の待ち時間がもったいない。
ならば、seedの変更が会った時だけ実行するようにしようというのが敬意です。
方針
やり方はいろいろあると思いますが、seedファイルの変更を検知するために、seedファイルのmd5のハッシュ値をとっておいて、それが切り替わったら実行するというやり方で実現してみました。
実物
以下のように、独自で定義したcapistranoのタスクを、deploy:migrate
の後で実行するようにします。
# 〜中略〜
# deploy用のスクリプトで、migrateの後にdeploy:seedタスクを実行させる
after 'deploy:migrate', 'deploy:seed'
# 〜中略〜
実際に、seedを実行するタスクは以下のようにして、seedファイルのmd5を保持しておき、それを比較して違っていたらseedを実行するような形で実現します。
# 〜中略〜
# seedの実行タスクは以下の通り
# 前提となる情報
# release_path: deployされたアプリのパス
# shared_path: 別のリリース時でも共有できる共有ディレクトリのパス
# seedファイルは、実行されるたびに、トランザクションを貼って洗い替え(delete & insert)することで、冪等性を担保している。
desc 'Load seed data into database'
task :seed do
on roles(:db) do
within release_path do
with rails_env: fetch(:rails_env) do
command = 'db:seed'
# rakeのコマンドを元に、seedファイルの相対パスを組み立て
seed_filepath = 'db/seed.rb'
new_digest_filepath = shared_path.join("new_#{seed_filepath.gsub('/', '_')}.md5")
digest_filepath = shared_path.join("#{seed_filepath.gsub('/', '_')}.md5")
# digestがなかった時にエラーにしないために、事前にtouch
execute :touch, digest_filepath
execute :md5sum, "#{seed_filepath} > #{new_digest_filepath}"
# md5が違っていたらseedを反映するコマンドを実行する
execute "if [ \"`cat #{new_digest_filepath}`\" != \"`cat #{digest_filepath}`\" ]; then cd #{release_path}; RAILS_ENV=#{ fetch(:rails_env) } bundle exec rake #{command}; mv #{new_digest_filepath} #{digest_filepath}; fi"
end
end
end
end
end