GraphQLはresolverを再帰的に実行するため、仕様としてN+1問題を起こしやすく、パフォーマンス・チューニングは重要になってくるかと思います。
そして、実際のデータ操作を行うのがresolverになりますのでresolverごとにパフォーマンスがわかれば便利です。
僕はLaravelでGraphQLを扱うために便利なLighthouseというライブラリを使っているので、例としてはLighthouseでresolverごとにパフォーマンスを計測をする方法について、簡単にまとめました。
クエリー自体の重さを計測するのは別のライブラリとかでできるので、それと組み合わせていければはかどる気がしています。
Apollo TracingというGraphQL自体の拡張機能を使っているので、node.js, Ruby, Scala, Java, Elixir, .NET等でも使えるみたいです。
まだまだ理解が足りない部分や誤解している箇所がありましたらぜひ教えていただけると助かります!!
開発環境
- 言語:PHP7.4.8
- フレームワーク:Laravel7.28.3
- DB:MySQL8.0.22
- Lighthouse:4.15
やり方
このconfig/app.php
に下記の記述を加えるのみです。
'providers' => [
\Nuwave\Lighthouse\Tracing\TracingServiceProvider::class,
],
そうすると、こちらのようにクエリーの下のほうにextensionsという項目ができ、それぞれのresolverの重さを計測をしてくれます。
"extensions": {
"tracing": {
"version": 1,
"startTime": "2017-07-28T14:20:32.106Z",
"endTime": "2017-07-28T14:20:32.109Z",
"duration": 2694443,
"parsing": {
"startOffset": 34953,
"duration": 351736,
},
"validation": {
"startOffset": 412349,
"duration": 670107,
},
"execution": {
"resolvers": [
{
"path": [
"hero"
],
"parentType": "Query",
"fieldName": "hero",
"returnType": "Character",
"startOffset": 1172456,
"duration": 215657
},
{
"path": [
"hero",
"name"
],
"parentType": "Droid",
"fieldName": "name",
"returnType": "String!",
"startOffset": 1903307,
"duration": 73098
},
pathや自分の型、フィールド名、返却する型等を取得をできます。
startOffsetのところの時間の単位はRFC3339の形式です。
重要なのはdurationで、こちらの単位はナノ秒です!
下記のページがGraphQLの拡張機能のapollo-tracingのページになり、詳細が記載されているので参考になります。
https://github.com/apollographql/apollo-tracing#response-format
今までのやり方(参考)
clockworkでクエリー自体の重さはわかるのですが、クエリーで取得をする量が多くなってくると、似たようなクエリーが多くなり、どこで発行をしているのかがあまり分からなくなってきてしまいました。
そこでどのresolverが重いか判別をするためには、計測したいクエリーをコメントアウトして、キャプチャのような感じで毎回chromeのdevツールでTimeがどれくらい変わるかを計測をしていました。
かなり微妙な気もしていて、とても面倒でした。
最終的にクライアントにextentionsを見せないようにするとか考慮する必要はあると思いますが、他のツールと組み合わせればパフォーマンス・チューニングが今までよりかかなりはかどりそうです!!