この記事について
Railsアプリを本番運用していると、「なぜかAPサーバのメモリ使用率が徐々に上がり続ける」といった事象に遭遇することがあります。
本記事では、
- APサーバのメモリが逼迫した実例
- メトリクスからの切り分け方法
- 実際に問題となっていたRailsコード
を整理してまとめます。
Railsアプリを運用している方の参考になれば幸いです。
前提
- CSチームが利用している内製のタスク管理システム(以後「カード」と呼びます)
- フロントエンド:Vue.js
- サーバサイド:Rails
- インフラ:AWS Elastic Beanstalk
- EC2構成
- r7g.large(2vCPU / 16GiB)
- Web と worker の両方を1台で担う構成
- 利用状況
- 1日あたりのアクティブユーザ:約50人
- 起票されるカード数:150〜200枚 / 日
発生していた問題
- メモリ使用率が90%を超えるアラートが度々発砲されるようになった
- 朝一にサーバ再起動で対応していたが根本解決には至らず
- メモリ逼迫時はアプリ再起動でしのぐ運用が常態化
情報収集:メトリクス確認
- 毎日8:00〜9:00にメモリ使用率が上昇
- 朝一(7:00)の再起動後から8:00までは安定
- 休日(12/7)は終日安定
考察(仮説)
- 休日が安定 → バッチ処理の可能性は低い
- 業務開始時間(8:00)から上昇 → 利用者導線上(カード一覧表示、詳細表示あたり)に問題がある可能性が高い
原因特定
- カード詳細表示時に、
Select *で大量の knowledges を取得しているSQLを発見- 蛇足だけどIN句の量が多いのもちょっと気になりました(indexが効かなくなる可能性)
- コードを見ると、ナレッジのコンテンツを含んだ重たいインスタンスを配列に積み上げているので、大量のメモリを消費していると思われる
該当コード
- app/view/cards/show.json.jbuilder
json.recommend_knowledges do
json.array! @card.recommend_knowledges do |knowledge|
json.id knowledge.id
json.title knowledge.title
json.url knowledge.url
end
end
- app/model/card.rb
def recommend_knowledges
recommend_tags = []
Tag.includes([:knowledges]).where(tag_type: 'knowledge').find_each do |tag|
next unless self.abstract&.include?(tag.name)
recommend_tags << tag.knowledges
end
recommend_tags.flatten.uniq
end
対応
- knowledgesは必要なカラムのみに絞って取得するようにすべき
-
show.json.jbuilderを見るとid,title,urlしか利用していない
-
- 今回は機能の利用実態を確認した結果、ほぼ使われていない機能と判明したので機能そのものを削除しました
結果
学び
- Rails(というかRuby)アプリはメモリ肥大化の問題をかかえていますので、その事を念頭に一気にメモリを使わないように常に心がけて実装する必要があります
- 最近はクラウドやフレームワークの成熟、さらにAIの進化によって、機能を簡単に実装できるようになり、内部の挙動やリソースを意識する機会が減っていると感じます
- しかしproductionのワークロードでは、データ量・同時アクセス・負荷・コストといった制約は常に存在しますので、こうした問題に向き合うためには相変わらず基礎的な知識は必要だなと思いました
参考記事
お知らせ
2026/1/28(水)に博多でエンジニア向けのLT会開催します。
お食事&ドリンクでワイワイする会です。興味のある方は是非お越しください。
https://layered.connpass.com/event/378115/
また若手エンジニア募集しています。
自社サービスのフルスタックエンジニアや、要件定義・設計にも興味のある方は是非。
https://www.wantedly.com/projects/2297977


