本番環境で動的コンパイルからアセットプリコンパイルに乗り換えた話

# 初めに
筆者は、スタートアップで資金繰りに困っている人をサポートするようなサービスの開発を行なっています。
普段からブログや記事を書きまくっている訳ではないので、文章のクオリティ低いことは目を瞑って読んでいただけると幸いです。

今回記事を書こうと思ったきっかけは、本番環境でプリコンパイルをせずに「動的コンパイル」のみで運用していた為、
デプロイ直後に504タイムアウトエラーに遭遇した筆者の体験を生かすべく備忘録として残そうと思いました。

意外とデフォルトで有効な設定値である「動的コンパイル」だけ利用している人も多いのでは？（違ったらごめんなさいw）と思ったので、
心当たりがある方はもちろんのこと、そうではない方も是非一読いただけると嬉しいです🙌

# 本番環境で動的コンパイルオンリーな運用はやめよう
なぜ、動的コンパイルオンリーな運用がダメなのか？
それは、「とある変更内容のデプロイ」をした後にタイムアウトエラーになる可能性があるからである。

とはいえ、「とある変更内容のデプロイ」って何だよ！！ってなりますよね。
「とある変更内容のデプロイ」とは、動的コンパイル処理するのに時間がかかるようなassetsファイルのデプロイを指しています。
つまるところ、画像やらJSなどのファイルをたくさんコンパイルしなきゃならない時ってことだね。

そして、沢山のファイルをコンパイルしている間にユーザがアクセスすると504タイムアウトエラーになります。

# 504タイムアウトエラーになってしまう根本要因
とても簡潔に根本要因を挙げるとプリコンパイルをしていない事です。

それでは、根本要因であるプリコンパイルしていないと、タイムアウトエラー発生の可能性を上げてしまう理由について、動的コンパイルの仕様を踏まえて説明します。
前提として、動的コンパイルの仕様は以下になります。
> `public/assets`のなかに必要なファイルを見つからなかった時に、`app/assets`などからファイルを探しコンパイル

[Railsドキュメント参照](https://railsdoc.com/page/config_assets_compile#:~:text=public/assets%E3%81%AE%E3%81%AA%E3%81%8B%E3%81%AB%E5%BF%85%E8%A6%81%E3%81%AA%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E8%A6%8B%E3%81%A4%E3%81%8B%E3%82%89%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F%E6%99%82%E3%81%AB%E3%80%81app/assets%E3%81%AA%E3%81%A9%E3%81%8B%E3%82%89%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E6%8E%A2%E3%81%97%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB)
仕様の補足をすると、`public/assets`配下にはプリコンパイルされたファイルが格納されます。
さらに、既にコンパイルされているassetsファイルは動的なコンパイル対象からは除外されます。

そして仕様から以下のことが言えます。
プリコンパイルしていないような環境下であると、動的なコンパイル処理がまだコンパイルしていないassetsファイルに都度走ることとなる。

その結果、プリコンパイルをせずに動的コンパイルのみの運用をしてしまうと、
タイムアウトエラーになる可能性を上げてしまうのである。

# プリコンパイルをする運用の具体案
タイムアウトエラーを発生させない為に、プリコンパイルをする運用の具体案を出したいと思います。
色んな方法やタイミングでプリコンパイルさせる方法がありますが、
今回は筆者が触っていた環境に準拠した、Capistranoデプロイタスクの一部としてプリコンパイルさせる方法案について筆者の体験をもとに説明します。

## Capfileにとある１行を追加するだけ？
```ruby:Capfile
require 'capistrano/rails/assets'
```
はい簡単！！上記1行加えたら、`assets:precompile`タスクが走るはずでした。。。
（Capistranoの設定環境によってはこの設定だけで済むので悪しからず）

しかし、筆者の環境では`assets:precompile`タスクが実行されることはありませんでした😭
※ `assets:precompile`タスクとは、この記事内で触れられている「アセットプリコンパイル」をしてくれるタスクに当たります
## なぜ`assets:precompile`タスクが走らなかったのか？
実はcapistranoで追加されたデプロイタスクのデフォルトロールが(:web)で、筆者が普段デプロイ時に割り当てていたのが(:app)ロールだったんですよね。([capistrano-rails githubのREADME参照](https://github.com/capistrano/rails#readme:~:text=%23%20Defaults%20to%20%5B%3Aweb%5D%0Aset%20%3Aassets_roles%2C%20%5B%3Aweb%2C%20%3Aapp%5D))
※ capistranoのロールとは、定義されているデプロイタスクに対して実行タスクを区分けするために利用するためのロール（詳しくは[こちら](https://capistranorb.com/documentation/advanced-features/role-filtering/)）

よって、普段は(:app)ロールだけ使用している人は以下の一行も加えましょう。(他の方法もありますが悪しからず🙏)
```ruby:deploy.rb
set :assets_roles, [:app]
```
※ 環境によって設定を変えたい方は適宜配置箇所など変えてください。
(筆者はデプロイする環境全てに反映させたかったので`deploy.rb`に定義しました)

## 動的コンパイルの設定をOFFにする
以下のコードのように動的コンパイルを無効にする為に、`config.assets.compile`の値を`false`に変更
```ruby:config/environments/production.rb
config.assets.compile = false
```
※ 本当は他の環境にも適用しましたが、長くなるので`production`環境だけ例に出しています

上記の設定をした背景を捕捉させていただきます。

Capistranoのデプロイタスクの中でプリコンパイルを自動化した為、`public/assets`配下には常にプリコンパイルされたファイルが格納されることとなります。
そして、動的コンパイルの仕様は`public/assets`配下にコンパイルされたファイルがない時に処理が走る仕様である為、次のように言えます。
それは、「プリコンパイルが常に自動でされる環境下では動的コンパイルの設定は不要」である。

よって、筆者の環境では動的コンパイルの設定をOFFにしました。

## Capistranoの設定DONE:rocket:
以上でCapistranoによるプリコンパイルの設定は終わりです。
設定がうまく反映されているか確認してみましょう。

以下2点が確認できればCapistranoによる自動プリコンパイルの設定が出来ていると言えるでしょう。
1. `deploy:assets:precompile` がCapistranoのデプロイタスクの中で実行されている
1. `public/assets`配下にコンパイルされたファイルが入っている

設定の反映がうまく出来ていることを祈ります。
# 終わりに
ここまで読んで頂いた方ありがとうございます！
Railsに慣れ浸しんでいる方には当たり前すぎる内容だったと思います。

プリコンパイル偉大だなって思わされましたし、自分が無知であることも思い知りました。
今回調べたりした中のほとんどの情報が、Railsガイドなどにも載っている説明のレベルの話だった為、
Railsガイドめちゃくちゃ重要ってことも再認識させられました😅

そんなこんなで、Railsガイドの社内輪読会を推進することでよりRailsの知見を深めております！

# 参考URL
- [config.assets.compileのデフォルトの値 - github](https://github.com/rails/sprockets-rails/blob/master/lib/sprockets/railtie.rb#L130)
- [アセットをプリコンパイルする - Railsガイド](https://railsguides.jp/asset_pipeline.html#%E3%82%A2%E3%82%BB%E3%83%83%E3%83%88%E3%82%92%E3%83%97%E3%83%AA%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB%E3%81%99%E3%82%8B)
- [動的コンパイル - Railsガイド](https://railsguides.jp/asset_pipeline.html#%E5%8B%95%E7%9A%84%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB)
- [capistranoドキュメント](https://capistranorb.com/)