【Github Actions】ci_index/ci_total & parallel_rspecを使ったCI高速化【Rails】
「RSpecのCIを早くできないんじゃ・・」
そんな同志にこの記事を捧ぐ・・😌
TL;DR
- GitHub Actionsのci_index/ci_totalとparallel_rspecを組み合わせたらCIでのRSpec実行時間を短くできた
ことの経緯
業務において私を含めた開発メンバーが最も触るRailsリポジトリがあるのだが、rspecをparallel_testによって2並列で実行しても20分程度かかるという状況だった。
とりあえず単純にparallel_testの並列数を3、4と上げてみたが実行時間は18分と、大した改善にならない・・。
そこで色々調べて、GitHub Actionsのci_index/ci_total と parallel_rspecの組み合わせを試してみると(cacheがヒットした時で)3分半程度まで短縮できたので、この記事でシェアしようと思う。
ci_index/ci_totalの指定で何が起こるのか
ci_index
および ci_total
は、GitHub Actionワークフローにおいてjobs.{job_name}.strategy.matrix
下で指定できる項目だ。
この2項目を設定すると、そのjobを実行するインスタンスが ci_total
の数だけ立ち上がって並列実行される。
ci_index
は、そのインスタンスそれぞれに割り振られるIDのようなイメージだ。
しかし、この2項目の指定だけでは並列実行されるインスタンスそれぞれで同じテストが重複して実行されてしまう。それぞれが重複ない形で、かつ全インスタンスでみると全てのテストが実行されるようにするにはもうひと工夫が必要だ。
parallel_rspecの--only-groupオプション
上で述べたような「もうひと工夫」はparallel_testを使ったrspecの実行の場合、--only-group
オプションの指定によって実現可能だ。
--only-group
オプションは、「(テスト全体をn分割した内の) m番目のグループの
テストのみを実行する」ように指示するもので、このグループ番号 m に実行インスタンスごとに値が異なるci_indexの値をそれぞれ指定することで、実質的に全テストの分散実行をすることができる。
実装のサンプル
以上の点を踏まえると、実装のサンプルとしては以下のようになる。
name: RSpec CI
on: pull_request
jobs:
test:
runs-on: ubuntu-latest
strategy:
# あるインスタンスでテストが失敗し停止した際に
# 他のインスタンスが停止しないようfail-fastをfalseにする
fail-fast: false
matrix:
ci_index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
ci_total: [16]
steps:
- uses: actions/checkout@v4
#
# ... (setup-rubyなどあれこれ)
#
- name: RSpec
env:
CONCURRENCY_INDEX: ${{ matrix.ci_index }}
PARALLEL_TESTS_CONCURRENCY: ${{ matrix.ci_total }}
run: |
bundle exec parallel_rspec \
-n $PARALLEL_TESTS_CONCURRENCY \
--only-group $CONCURRENCY_INDEX
参考
Discussion