RailsやRSpecの起動が遅いと感じたときの高速化テクニック
ローカル環境のRailsやRSpecの起動を早くするためのテクニックについて説明します。開発環境の状況によりうまく適用できないケースもあると思いますができるだけ汎用的な手法について説明しています。
開発をしていてRailsやRSpecの起動がなんか遅いなーと感じることはないでしょうか。
はじめは遅いと思っても次第に慣れていってしまいがちですが、日々の開発を通してRailsやRSpecの起動は何回も実行するので塵も積もれば山となってしまうので改善したいものです。もしあなたの環境で常に起動までに2-3秒以上かかっているのであれば黄色信号だと思います。
この記事では、ローカル環境のRailsやRSpecの起動を早くするためのテクニックについて説明します。開発環境の状況によりうまく適用できないケースもあると思いますができるだけ汎用的な手法について説明しています。
ちなみに、私が携わったとあるプロジェクトで改善して次のように高速化できました。
rails
コマンドの実行時間が「10秒から0.6秒」rspec
コマンドの実行時間の「23秒から1.1秒」
ぜひ、参考になれば嬉しいです。
- 1. spring gemでRailsの起動時間を速くする
- 2. spring-commnads-rspec gemでRspecの起動時間を速くする
- 3. bootsnapでRailsの起動時間を速くする
- 4. テストデータの作成タイミングを変えてRspecの起動時間を速くする
1. spring
gemでRailsの起動時間を速くする
Railsの起動速度を向上させる方法の1つにspringを使う方法があります。
springはRailsアプリケーションのプリローダーで、bin/rails
コマンドを実行すると自動的にバックグラウンドで実行されるので普段の開発で気にする必要はありません。
springがバックグラウンドで動くことで、初回のrailsの起動は遅いのですが、2回目以降は事前にロードされるので起動がかなり早くなります。
以下は、とあるプロジェクトで実際にspringをいれたときのrails runner
の時間です。10秒程度かかっていたのが0.67秒まで短縮されています。
# springを入れる前
time bin/rails runner "puts 'hello, world'"
4.73s user
2.92s system
10.780 total
# springを入れた後 (※2回目のコマンド実行時)
time bin/rails runner "puts 'hello, world'"
0.08s user
0.06s system
0.673 total
spring
のインストールとセットアップ方法は次の通りです。詳細は、READMEをご覧ください。
まず、Gemfile
にspring
を追加して、bundle install
を実行します。
gem "spring", group: :development
そして、spring binstub --all
コマンドを実行すると、bin/
ディレクトリ配下のファイルが更新され、bin/rails
などを実行するとspringが自動で実行されるようになります。
$ bundle exec spring binstub --all
springの注意点としては、springがうまくインストールできなかったり、springを使うと一部テストがこけるケースが稀にあります。また、Dockerと一緒に使うにはうまくspringが動かないケースもあったりするのでご注意ください。
参考: Using Spring with a containerized development environment
2. spring-commnads-rspec
gemでRspecの起動時間を速くする
Rspecの起動速度を向上させる方法の1つにspring-commnads-rspec gemを使う方法があります。これはspring用のrspec
コマンドです。
こちらもbin/rspec
と実行することで自動的にspringがバックグラウンドで起動し、2回目以降のbin/rspec
コマンドの起動時間を1秒以内のスピードまで早めることができます。
spring-commands-rspec
のインストールとセットアップ方法は次の通りです。
まずGemfile
にspring-commands-rspec
を追加して、bundle install
を実行します。
gem 'spring-commands-rspec', group: :development
次に、bundle exec spring binstub rspec
を実行することで、bin/rspec
コマンドが作成されます。
$ bundle exec spring binstub rspec
3. bootsnap
でRailsの起動時間を速くする
bootsnap は負荷が高い計算を最適化とキャッシュをすることで起動時間を短縮します。
Shopifyが開発したgemで、とあるShopifyの大きなアプリケーションでは、起動時間が約25秒から6.5秒と75%早くなったという結果があります。
インストール・セットアップ方法は次の通りです。
まず、Gemfile
にbootsnap
を追加します。
gem 'bootsnap', require: false
そして、config/boot.rb
にrequire 'bootsnap/setup'
を追加します。
require 'bootsnap/setup'
bootsnapの仕組みについてはREADMEのHow does this work?にわかりやすく記載されていますが、ざっくり次の2つのことをしています。
- パスを事前にスキャン:
Kernel#require
とKernel#load
が修正され、$LOAD_PATH
スキャンを実施しなくする。 - コンパイルのキャッシュ: Rubyのバイトコードの結果をキャッシュしたり、YAMLオブジェクトのロード結果をMessagePackのフォーマットでキャッシュする。
4. テストデータの作成タイミングを変えてRspecの起動時間を速くする
テストデータの生成に10秒ほどかかっており、bin/rspec
コマンドの実行からテストの実行が開始されるまでの時間がかかるケースがありました。そういった場合は、テストデータの生成タイミングを変えることでRspecの起動時間を高速化できます。
例えば、次のようにテストスートの実行前に毎回実行時間がかかる初期データの生成をしていたとします。
RSpec.configure do |config|
# テストスートの実行前の処理
config.before(:suite) do
# 時間がかかる初期データの生成処理(イメージ)
Seed.generate(fixture_paths)
end
end
そういった場合は、テストの実行ごとに初期データを生成するのではなく、テスト環境のセットアップ時に初期データを生成できるようにするのがおすすめです。テスト時には、トランザクションやdatabase_rewinderを使うことでテストごとにデータベースの状態を戻すので問題になるケースはほとんどないでしょう。
# テスト環境のセットアップ時に初期データを生成する(イメージ)
$ RAILS_ENV=test bin/rails runner "Seed.generate(fixture_paths)"