Railsに蔓延るN + 1問題を解決する為に、Serializerとincludesを1 : 1にするmoduleを作った

こんにちは、taskeyの田代です。

弊社サービス

21年始まってしまいましたが、20年でやったことで書いていないことがあったので、書き残していくチェレンジ THE FINALです。

何気に初めてRailsでの実装に関して書かせていただきます。

N + 1っていつの間にか増えてませんか?
勿論、機能開発時にtest環境でもbullet入れてますし、debug段階でlogの確認もしております。
それでも広範囲の改修等で、何故か出てきてしまうアイツです。
そのまんま放置しているとAPI速度低下の原因になってしまいます。

何が起きていた?

まずはイメージが湧きやすいようにModelの説明からさせていただきます。
弊社はテキストを中心に、映像やイラストで表現された新しい読書体験が可能なe-Storyを提供するサービスです。

アプリを見て頂けたら大体のイメージが付くと思うのですが、 「作家 has_many 作品 has_many 話 has_many コンテンツ」のようなリレーションを持っていて、実際はこれらが更に細かく広がっています。

当たり前ですが、全てのリレーションをjoinして各attributeをアプリ側へ返却してしまうと、APIのレスポンス速度もサイズも肥大化してしまい、とても本番運用出来ないような実装になってしまいます。

そのため、APIごとにincludesする内容を変更しているのですが、度重なる機能開発により、これらに漏れが生じていました。

Serializerとincludesの内容って大体1 + 1になるのでは?

そんなことを思いつき、その為のmoduleを作成することにしました。
これが出来たらincludesを一箇所で管理出来るし、必要なリレーションも明示的にわかりやすくなります。

長期的には、アプリ側で使われていないkeyを洗い出して貰い、更にSerializer、includesを細かく整理していくのも大切です。
それらを行う上でも、このmoduleを有用そうです。

実装紹介

めちゃめちゃシンプルです。

module StoryIncludable
 def story_includes_by(story_model, serializer)
   case serializer.name
   when 'StorySerializer'
     story_model.includes(
       :author,
       :tags,
       :ranking,
       chapters: :contents
     )
   # 以下Serializer毎の各種リレーション
   else
     raise StandardError, "Unsupported serializer: #{serializer}" # カスタムエラーでも良い
   end
 end
end

story_includes_byにモデルとserializerを渡すと、必要なincludesをしてくれます。
 ※ モデルを渡す必要無いのでは?と思いますが理由あってのことです。説明は割愛させていただきます。

 - Serializer内でattributeが変更された場合に、ほとんどのケースで一つのAPIを確認するだけでN + 1の蔓延を阻止する事が出来る
 - 一箇所での管理になるので、単純にModel設計の把握がしやすくなる
    ※ アプリ側と協力してチューニングする際に役に立ちそう
 - デグレが怖い場合、このmoduleを導入しないという選択も出来る

シンプルなmoduleなのにウレシイ事ずくしです。ヤッター
今後、ActiveModelSerializersをwrapしてしまうgemを作っても良いかもしれませんね。

終わりに

普段の機能開発ってなかなか記事にしにくいのですが、今回は改修ということもあり、Railsの記事を書けたので個人的にとても嬉しいです。

勿論他のアプローチ方法や、gemでの解決方法もあるかもしれません。
この記事を読んで思うことがありましたら、引用RTでも何でも頂けますと幸いです。

このアプローチでgemを作ってしまってもいいので、誰か手が空いたら作っちゃってください。僕も暇な時軽く着手してみようかな?とも思ってます。

募集

弊社正直、エンジニアが足りてません!!!

Serverエンジニアに関して、下記現状です!!!
ご興味有る方是非ご連絡くださいー!!!

アピールポイント

・ Spec整備済み
・ OpenAPI 整備済み
・ 機能開発毎にエンジニアで設計や命名を話し合う風習あり
・ データマート(BigQuery)整備済み
・ Deployフロー整備済み
・ 負債に対しての定期的なMTGと、それを解像度高く分解して実行できる環境
・ ECS on Fargate、Aurora Auto Scaling とモダンなインフラを採用
・ データ分析、SRE、フロントエンド(React)、リコメンドエンジン等の幅の広いスキルの拡張が可能

現状の課題

・ 現状がRails5系、Ruby2.5系と、最新のバージョンへの追従が遅れている
=> 直近でVersionUP予定あり
・ APIで返却するJSONが最適化されていない
=> JSON整備予定あり
・ JSONResponse返却のGetRequestに対してのCDN整備が整っていない
=> 長期的観点でCDN導入予定
・施策速度優先とエンジニア不足により技術的負債を解消し切れていない  => DX、開発効率を上げていくためにも必須

この記事が気に入ったらサポートをしてみませんか?