GraphQLの「Resolver並行性・I/O並列性」の言語別実装比較(JS, Go, Ruby, PHP)
はじめに
GraphQLは柔軟なデータ取得を可能にする一方で、N+1問題やI/Oによるレスポンス遅延が課題になることが多いです。
このような課題を解決するために、各言語・ライブラリが「Resolverの並行実行」や「I/Oの並列化」などの工夫をしています。しかし、その実装方式は言語ごとにかなり違いがあります。
この記事では、graphql-js(Node.js)、graphql-go(Go)、graphql-ruby(Ruby)、graphql-php(PHP)の主要なGraphQLサーバー実装を比較します。
なぜResolverの並行性が必要なのか
GraphQLでは、クライアントから柔軟かつ複雑なデータ取得要求(クエリ)が送られることが多く、1つのクエリが多数のResolver(データ取得処理)を呼び出す構造になります。
このとき、Resolverの並行性(複数のResolverを同時に処理できること)が重要な理由は主に以下の3点です。
レスポンス速度の向上
- 多数のResolverが順番に(直列で)実行されると、全体のレスポンス時間が合計になり、遅くなってしまう。
- I/Oを並列しない場合、例えばあるResolverが利用するAPIが100msかかる時にレスポンスが返るまで待つ必要があり(同期的)、他の処理は進める事ができない。
- 並行実行できれば、複数のI/O(DB・API・ファイルアクセスなど)を同時進行できるため、最も遅い処理時間が全体のレスポンス時間となり、高速化できる。
N+1問題の対策
- GraphQLのクエリ構造上、「親→子→孫」で同じデータ取得処理が大量に呼ばれる(N+1問題)が発生しやすい。
- 並行性のあるResolver設計やDataloaderと組み合わせることで、大量のI/Oリクエストをまとめて効率よく処理できる。
サーバーリソースの有効活用
- サーバーのCPUやI/O帯域を最大限活用できる。
- 非同期・並行処理によって、待ち時間の間に他の処理を進められるため、スループットが向上し、同時アクセス増にも強くなる。
Resolverの並行性は、GraphQLサーバーのパフォーマンス・スケーラビリティ・ユーザー体験の向上に直結する重要な技術的ポイントです。特に、外部APIやDBアクセスが多いサービスでは、並行処理の最適化がレスポンス品質を決定づけます。
言語ごとの並行・並列モデルの特性
GraphQLのResolverやI/O処理の並行化は、言語ごとのランタイムや並行モデルに強く依存します。ここではそれぞれの言語が持つ仕組みや制約を簡単にまとめます。
JavaScript(Node.js)
- イベントループ+Promise/async/awaitによる非同期・並行処理が標準。
- シングルスレッドだが、I/O(ファイル・ネットワーク)はイベントループでノンブロッキングに並行処理される。
- ResolverでPromiseを返すと並行処理が行える。異なる高さのResolverでも並行処理が可能。
Go
- goroutine(軽量スレッド)が言語レベルでサポートされている。
- goroutineは数万個でも低コストで並行実行可能。I/Oもgoroutineで非同期化しやすい。
- goroutineで実行する事でResolverの並行実行、I/O処理の並列化が可能。
Ruby
- マルチスレッドがサポートされているが基本的にはGVL(グローバルVMロック)により基本的には処理は1つのスレッドの処理しか実行されない。
- ただし、RubyはI/O(DB, HTTPなど)が発生する際にGVLを解放し、他のスレッドに処理を移行するため、複数スレッドでI/O並行化が可能。
- graphql-rubyは基本的にはResolverを上から下へ順次実行する。
- Ruby 3.0以降、Fiber Schedulerやasync gemで「協調的並行I/O」ができるようになり、graphql-rubyではAsyncDataloaderを使う事でResolverの並行実行が可能になる。
PHP
- 基本的にシングルプロセス・同期実行モデル。
- 一般的なPHP環境では、ひとつのリクエストにつきひとつのプロセスで全ての処理が順番に実行される。
- graphql-phpはResolverを順次実行する。並行実行する実装はまだない。
- amphpやReactPHPなどの非同期ライブラリを使えば部分的に非同期I/Oは可能
各実装の並行性・I/O並列性・Resolverの解決順の比較
実装 | Resolverの並行実行 | Resolver内でのI/O並列性 | Resolverの解決順(深さ) | 備考 |
---|---|---|---|---|
graphql-js | あり(Promise/async/await) | あり(Promise/async/awaitで複数I/O) | 異なる階層でResolverを並行実行 | JSの非同期モデルを活用 |
graphql-go | あり(goroutineで並行可能) | あり(goroutineで複数I/O並列化可能) | 階層毎に順次処理 | Goの並行性モデル |
graphql-ruby | 基本なし(AsyncDataloader利用時のみ) | 基本なし(AsyncDataloaderのみあり) | 階層毎に順次処理 | Fiber/async gemでI/O並列化 |
graphql-php | なし(同期実行) | なし(自作で並行化しない限り不可) | 階層毎に順次処理 | PHPは非同期未対応 |
クエリツリーの解決にかかる時間
JavaScript(Node.js)
ツリー構造の各ノード(フィールド)のResolverが親から子へと非同期・並行に処理され、全体の応答時間は「GraphQLクエリツリー内の最長クリティカルパス(critical path)」に依存する。
つまり、最も時間のかかる末端ノードまでの経路の合計実行時間が、レスポンス全体のレイテンシとなる。
Go言語
GraphQLクエリツリーの各階層(同じ深さ)に属するノードのResolverがgoroutineによって並行に処理され、全体の応答時間は「各階層ごとに最も時間のかかるクリティカルパス(critical path)」の合計に依存する。
つまり、各階層で最も重いResolverの実行時間を積み上げた値が、レスポンス全体のレイテンシとなる。
Ruby
GraphQLクエリツリーの各ノードのResolverが階層(親から子)ごとに逐次的(直列)に処理されるため、全体の応答時間は各階層のResolverの合計実行時間に依存し、並行性はほとんど得られない。
つまり、親Resolverの処理が完了してから子Resolverが順に実行されるため、レスポンス全体のレイテンシは直列実行の合算となる。
AsyncDataloaderを利用した場合は、各階層の複数ResolverやI/O処理をFiber Schedulerやasync gemによって並行・非同期に処理できるため、各階層ごとに最も時間のかかるクリティカルパス(critical path)の合計がレスポンス全体のレイテンシとなる。
つまり、同じ階層内のResolverやI/Oを並行実行することで、レスポンス速度を大幅に向上させることが可能となる。
PHP
親から子へと逐次的(直列)に同期的に処理されるため、全体の応答時間は各Resolverの合計実行時間に完全に依存し、並行性・非同期性は基本的に得られない。
つまり、レスポンス全体のレイテンシは全てのResolverの直列実行の合算となる。
まとめ
JavaScript(Node.js) はGraphQLのResolverの並行性を最大限に活かしたい場合、現状最適な選択肢と言えます。JavaScriptはイベントループとPromise/async/awaitにより、階層や深さに関係なく複数のResolverやI/O処理を自然に並行実行でき、レスポンス速度・スケーラビリティの面でも優れています。
Go言語 もgoroutineとchannelによる強力な並行処理モデルを持ち、Resolverの同一階層での並行実行が行えます。
Ruby は、標準ではResolverの並行実行は弱いですが、AsyncDataloaderやFiber Scheduler、async gemを活用すれば、I/O並行化も実現可能です。ただし、エコシステムやサポート状況、実装の複雑さを考慮すると、JSやGoには一歩譲る部分があります。
PHP は基本的にシングルプロセス・同期実行モデルのため、GraphQLでのResolverの並行実行は難しいのが現状です。amphpなどの非同期ライブラリを組み合わせることで部分的に並行化は可能ですが、2025年現在では公式サポートやコミュニティの標準パターンにはなっていません。今後に期待したいです。
結論
GraphQLで「複雑なクエリを高速に・効率よく」処理したい場合は、JavaScriptがもっとも並行性の恩恵を受けやすく最適です。Goも同一階層での並行処理が行えるため並行性では優れてと言えるでしょうう。Rubyでは公式で手段が用意されているため、やろうと思えば可能です。PHPは現状では困難というのが実情です。
宣伝
合同会社春秋ではWebアプリケーションやクラウドの開発支援を行なっています。Ruby on Rails, Laravel, React.js, Vue.js, GraphQLなどを強みとしています。開発や設計、技術導入支援、GraphQLの導入・高速化のなど、お仕事募集してます。
Discussion