Next.jsCI における
build cache高速化した話

@yKicchan

Draft
Next.js の CI における build cache を高速化した話

自己紹介

きっちゃそ
:DeNA / Pococha
Web Frontend
X @yKicchan
Next.js の CI における build cache を高速化した話

目次

  1. Next.js の CI 設定
  2. 事件発生
  3. 調査開始
  4. 解決編
  5. まとめ

1. Next.js の CI 設定

1. Next.js の CI 設定

環境情報

  • Next.js v14 - PagesRouter
  • GitHub Actions
  • PR の CI でビルドが走る

今日の本題は GitHub Actions に限った話

1. Next.js の CI 設定

GitHub Actions での CI 設定

uses: actions/cache@v4
with:
  path: |
    ~/.npm
    ${{ github.workspace }}/.next/cache
  key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}
          -${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }}
  restore-keys: |
    ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
1. Next.js の CI 設定

ビルドキャッシュの恩恵

  • ⌚️ビルド時間の短縮
    • ヒット時には 30s ~ 1m 程度早くなっていた
  • 💰ランナーのコスト削減
1. Next.js の CI 設定

時は過ぎてゆき...

2. 事件発生

2. 事件発生

CI が高確率でコケはじめる

  • PR によってコケたりコケなかったり
  • Re run で成功することも

VRT なども CI で走らせており、
ビルドの遅さに気づきづらかったという事情も

2. 事件発生

実際のエラー

error

hashFiles() が 120 秒かかってタイムアウト😱

2. 事件発生

たまたま見たやつ

no-cache

hit-cache

2. 事件発生

キャッシュない方が早くてァ!

2. 事件発生

目算での平均ビルド時間の比較

ステップ キャッシュなし キャッシュあり
キャッシュ検索 2分 1.5分 ~ 2分
ビルド 2.5分 2分
キャッシュ保存 1分 1分
合計時間 5.5分 4.5分 ~ 5分
2. 事件発生

キャッシュの処理が
ビルドCIの 約60% を占めている😱

つまりキャッシュを無くせば倍以上早くなる...w

3. 調査開始

3. 調査開始

ゴールの確認

  • hashFiles() のタイムアウトを解消
  • ビルドキャッシュの恩恵を"ちゃんと"受ける
3. 調査開始

hashFiles() のタイムアウトを解消

  • タイムアウトを伸ばす...?
    • エラーを回避するだけならこれでいいが...
    • ハッシュ計算に時間がかかっているのは事実
  • 対象ファイルを絞って高速化...?
    • ビルドキャッシュだし本末転倒感
    • テストファイルとかは除外できそう
3. 調査開始

タイムアウトを伸ばす

  • 残念ながらそんな設定はない
  • Issue は上がっているが放置気味...
  • 仮にできてもキャッシュ使わない方が早いので意味なし
  • なんとかして hashFiles() を高速化するしかなさそう
3. 調査開始

hashFiles() の高速化

  • hashFiles() はファイルのハッシュを計算する
  • ファイル数が多いと遅くなるのは当然
  • とりあえず hashFiles() の実装を見てみる
3. 調査開始

hashFiles() の実装

  • 検索パターンを受け取る
    • 複数あればそれを結合する
  • 一致するファイルを探索する
  • ファイルのハッシュを計算する

そwれwはwそwうwww

3. 調査開始

...なんか随所でログを出力してるな?🤔

3. 調査開始

log

3. 調査開始

ほぼ全てが node_modules じゃねぇか!

4. 解決編

4. 解決編

node_modules は除外する

  • node_modules はハッシュ計算の対象外にする
  • ただし、package-lock.json は対象にする
4. 解決編

node_modules の除外

uses: actions/cache@v4
with:
  path: |
    ~/.npm
    ${{ github.workspace }}/.next/cache
  key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}
          -${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx', '!node_modules/**/*') }}
  restore-keys: |
    ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-

npm ci 前にこのステップを移動でもOK

4. 解決編

改善後の目算キャッシュ時間の比較

ステップ Before After diff
キャッシュ検索 2分 20秒 -100秒
キャッシュ保存 1分 30秒 -30秒
合計 3分 50秒 約-2分

5. まとめ

5. まとめ

まとめ

  • Next.js の CI でビルドキャッシュが遅かった
  • hashFiles() の対象に node_modules が含まれていた
  • これを解消してビルドキャッシュを高速化した

Next.js に限らず、Node.js 系のプロジェクトは同じ可能性がある

5. まとめ

教訓

  • 困ったら debuglog を有効にしてみよう
  • 検索対象のファイルに node_modules が含まれていないか気をつけよう
  • 定期的に CI の実行時間は計測しよう
5. まとめ

元ネタ

zenn

EOF