もうずっといなかぐらし

かたいなかのブログ

NestJS製GraphQLサーバの起動が遅かったので調査した話

こんにちは、かたいなかです。

最近、NestJS製のGraphQLサーバでのSchemaからのTypescriptファイルの生成が遅い問題を調査する機会がありました。

今回の記事では、ツールの使い方等の自分への備忘録を兼ねて、どのように問題に取り組んだかを記事にして紹介します。

TL;DR

  • 高速化するならまずは計測から。計測によるボトルネック特定が最優先。
  • フレームグラフが遅い処理を特定するのに超便利。Node.jsなら0xがフレームグラフ生成に便利。
  • @nestjs/graphql でGraphQLのスキーマからTypescriptのファイルを生成する処理が速くなった。

経緯

最近関わった案件では、BFFとしてNestJS製のGraphQLサーバをスキーマファーストで使用していました。GraphQLによるスキーマファーストな開発の恩恵は日々感じているものの、サーバの起動時や再コンパイルの際に40秒ほど時間がかかってしまっていました。この待ち時間のために行った変更をローカルでサクッと動作確認することができず、フラストレーションが溜まる状態になっていました。

この問題について、GraphQLのスキーマからTypescirptへのコンパイル処理に時間がかかっているのではないか、ということまでは分かっていたのですが、詳しい調査は手つかずとなっていました。

まずは計測してボトルネックを特定

問題の調査にあたってはまず、起動時の処理のフレームグラフを取得し、時間がかかっている処理を特定することを目指しました。

Node.jsでフレームグラフを出すには 0x というツールが便利そうでした。

github.com

そこで、以下のコマンドで起動処理のフレームグラフを作成しました。

$ NODE_ENV=development 0x dist/main.js

フレームグラフをよく見ると、 @nestjs/graphqlgraphql-explorer.ast.ts というファイルから呼び出された ts-morph というTypescriptのAST変換のライブラリでの処理に時間がかかっているようでした。

更に調査

@nestjs/graphqlts-morph の使い方に問題がありそうなことはわかったので、具体的には何が問題なのかを詳しく調べてみます。

ts-morph の公式ドキュメントを見てみると、performanceに関するtipsがまとめられたページがありました。

ts-morph.com

そこで、このページの内容をもとに既存のコードをチェックしてみると、大きく以下の2つの問題があることがわかりました。

  • interfaceやproperty等を一つずつ追加している(このような処理はバッチで一度に行うべき)
  • Structure (簡易ASTを表すオブジェクトで高速化に寄与する)があまり活用されていない

問題修正

問題が特定できたので、早速コードを修正しました。

コードを修正して開発環境で試してみると、以下の画像のように40秒以上かかっていたサーバの起動が3秒程度で終わるようになりました!!!

before

after

PRを作成

実際にサーバの起動が高速化されることも検証できたので、修正PRを作成しました。

github.com

また、リリースを待つ間にも開発メンバーに高速化した起動を体験してもらえるよう、現在のBFFで使用している @nextjs/graphql に合わせたパッチファイルを作成し展開しました。

こちらは正式にリリースされたコードではないため、リリース前等に正式版の nextjs/graphqlスキーマを生成し直して、問題ないかを確認する運用にしています。

まとめ

今回の記事では、GraphQLの起動時間の問題に対してどのように取り組んだかの一連の流れを紹介しました。

ボトルネックの特定から実際のパフォーマンス改善まで一連の流れはNode.jsに限らず様々なパフォーマンス問題の調査で共通するものだと思っています。

この記事がどなたかの参考になれば幸いです。

参考