ridgepoleの起動をめっちゃ早くする
要約
新gem spring-commands-ridgepoleを作ったので使ってみてね。
Gemfileに
gem 'spring-commands-ridgepole', group: :development
をコピペするだけでinstallできるよ。
$ bin/spring binstub ridgepole
でbinstubを作って、
$ bin/ridgepole
で使えるよ。
ridgepole
ridgepoleという神ツールを皆様御存知でしょうか。
Rails migration的なAPIでテーブル定義を宣言しておくと、その通りにデータベースにテーブルを構築してくれるツールです。
RDB界のReactです。
プロジェクト超初期ではテーブル定義は試行錯誤を繰り返すのでめちゃくちゃ変更します。 宣言的にテーブルが書けて考えも整理しやすく、私はridgepoleを愛用しています。
しかし、悩みがありました。私の環境では起動が遅いということです。
実際に実行してみましょう。
$ time docker-compose exec api bundle exec ridgepole --env development --config config/database.yml --apply
Apply `Schemafile`
No change
docker-compose exec api bundle exec ridgepole --env development --config 0.40s user 0.10s system 1% cpu 37.273 total
37s
テーブル構成をあれこれ試しまくりたい。高速に開発しないとやりたいこと忘れちゃう。気がついたらtwitter見ちゃう。 そんな自分としては、これは悩みのタネでした。
spring
そこでピーン。前回のblog postを思い出しました。
https://ksss.ink/blog/posts/7-spring
もしridgepole起動の遅い原因がRailsコードの読み込みなのだとしたら、springで高速化するはずです。 特に計測もしていませんがやってみましょう。
spring-commands-*
springを使ったコマンドを作るのはハッキリ言って結構めんどくさいです。
なんか全コマンドいい感じになればいいのにな。
ともかくspringでコマンドを作るにはgemを作る必要があります。
# Auto-require any Spring extensions which are in the Gemfile
Gem::Specification.map(&:name).grep(/^spring-/).each do |command|
begin
require command
rescue LoadError => error
if error.message.include?(command)
require command.gsub("-", "/")
else
raise
end
end
end
このようにgemspecを読み込んでrequireするようになっているので、Gemfileでpath
指定するか、publicなgemとして作るしかありません。
とはいえ実装は短いです。
皆さんのアプリケーションのGemfileにもspring-commands-rspec等が忍ばれていませんか?
rspec用でも実装は実質これだけです。
module Spring
module Commands
class RSpec
def env(*)
"test"
end
def exec_name
"rspec"
end
def gem_name
"rspec-core"
end
def call
::RSpec.configuration.start_time = Time.now if defined?(::RSpec.configuration.start_time)
load Gem.bin_path(gem_name, exec_name)
end
end
Spring.register_command "rspec", RSpec.new
Spring::Commands::Rake.environment_matchers[/^spec($|:)/] = "test"
end
end
こうしてgemをinstallしておくだけで、springでbinstubを生成したり、springを土台にコマンドを実行できて高速化を図ることができます。
でもちょっと大変だった
ちょっとハマった点としては、
ridgepoleが、おそらく
ridgepole読み込み => Railsコード読み込み
という順番を想定しています。
しかしspringでは
springでRailsコード読み込まれてる => ridgepole読み込み
の順番になってしまうのでwithout_table_optionsがなくてNoMethodErrorになってしまいました。
この辺を理解するのがハマりました。理解できたら解消はすぐでした。
37s => 1s
ともかくがんばって作った成果はというと
$ time docker-compose exec api bin/ridgepole --env development --config config/database.yml --apply
Running via Spring preloader in process 1023
Apply `Schemafile`
No change
docker-compose exec api bin/ridgepole --env development --config --apply 0.39s user 0.09s system 37% cpu 1.273 total
37s => 1sと超高速化することができています!!!
私はdevelopmentとtest環境で同時に適用されるように
task :apply do |t|
["development", "test"].each do |env|
system "bin/ridgepole --env #{env} --config config/database.yml --apply"
end
end
とRake taskを組んでいるので、およそ1分かかっていたものが2秒位で終わります。記述も簡潔です。導入もgemをinstallするだけです。
これは革命。
1回あたり約1分短縮しているので、このgemを作るのにかかった2時間くらいを考えると、120回叩いたらペイします。
やったね。