大きなデータを解決するとき、リゾルバーからクライアントに結果を返す瞬間から、パフォーマンスが非常に遅いことに気づきました。
私はapollo-server
が私の結果を反復処理して型をチェックすると想定しています...
私の製品では、UIでチャートを描画するために一度に使用されるため、大量のデータを一度に返す必要があります。データをスライスできるページネーションオプションはありません。
私はapollo-server
が原因で速度が低下しているのではなく、リゾルバオブジェクトの作成ではないと考えています。
リゾルバーがオブジェクトを作成するのにかかった時間と、高速であり、ボトルネックではないことに注意してください。
測定方法がわからないapollo-server
によるその後の操作には、かなりの時間がかかります。
これで、カスタムスカラー型JSONを返すバージョンができました。応答ははるかに高速です。しかし、私は自分のSeries
タイプを返すことを本当に好みます。
ネットワークパネルを見て、2つのタイプ(Series
とJSON
)の違いを測定します。
aMOUNTが500に設定され、タイプがSeries
の場合、約1.5秒(つまり秒)かかります
aMOUNTが500に設定され、タイプがJSON
の場合、約150ミリ秒(高速!)
aMOUNTが1000に設定されていて、タイプがSeries
の場合、非常に遅い...
aMOUNTが10000に設定されており、タイプがSeries
の場合、JavaScriptヒープがメモリ不足になります(これは、残念ながら私たちの製品で発生していることです)
また、apollo-server
のパフォーマンスをexpress-graphql
と比較しました。後者は高速に動作しますが、カスタムスカラーJSONを返すほど高速ではありません。
aMOUNTを500に設定すると、apollo-server
、ネットワークに1.5秒かかります
aMOUNTを500に設定すると、express-graphql
、ネットワークに800ミリ秒かかります
aMOUNTが1000に設定されている場合、apollo-server
、ネットワークには5.4秒かかります
aMOUNTが1000に設定されている場合、express-graphql
、ネットワークは3.4秒かかります
スタック:
"dependencies": {
"apollo-server": "^2.6.1",
"graphql": "^14.3.1",
"graphql-type-json": "^0.3.0",
"lodash": "^4.17.11"
}
コード:
const _ = require("lodash");
const { performance } = require("perf_hooks");
const { ApolloServer, gql } = require("apollo-server");
const GraphQLJSON = require('graphql-type-json');
// The GraphQL schema
const typeDefs = gql`
scalar JSON
type Unit {
name: String!
value: String!
}
type Group {
name: String!
values: [Unit!]!
}
type Series {
data: [Group!]!
keys: [Unit!]!
hack: String
}
type Query {
complex: Series
}
`;
const AMOUNT = 500;
// A map of functions which return data for the schema.
const resolvers = {
Query: {
complex: () => {
let before = performance.now();
const result = {
data: _.times(AMOUNT, () => ({
name: "a",
values: _.times(AMOUNT, () => (
{
name: "a",
value: "a"
}
)),
})),
keys: _.times(AMOUNT, () => ({
name: "a",
value: "a"
}))
};
let after = performance.now() - before;
console.log("resolver took: ", after);
return result
}
}
};
const server = new ApolloServer({
typeDefs,
resolvers: _.assign({ JSON: GraphQLJSON }, resolvers),
});
server.listen().then(({ url }) => {
console.log(`???? Server ready at ${url}`);
});
Playgroundのgqlクエリ(シリーズタイプの場合):
query {
complex {
data {
name
values {
name
value
}
}
keys {
name
value
}
}
}
Playgroundのgqlクエリ(カスタムスカラータイプJSONの場合):
query {
complex
}
これが実際の例です:
https://codesandbox.io/s/apollo-server-performance-issue-i7fk7
どんなリード/アイデアも大歓迎です!
コメントの要約
このデータ構造/タイプ:
id
フィールドなし)。このようにこのデータセットはgraphQLが設計されたものではありません。もちろん、graphQLはこのデータのフェッチに引き続き使用できますが、型の解析/マッチングは無効にする必要があります。
カスタムスカラータイプ (graphql-type-json
)が解決策になることがあります。ハイブリッドソリューションが必要な場合は、Group.values
as json(代わりにSeries
全体)。正規化されたキャッシュ[アクセス]を使用する場合は、グループにid
フィールドが必要です。
apollo-link-rest
「純粋な」jsonデータ(ファイル)をフェッチし、タイプの解析/マッチングをクライアント側のみに残します。
1つのgraphqlエンドポイントを使用したい場合...独自のリンクを作成します-ディレクティブを使用します-'jsonを要求し、入力します'-上記の2つの組み合わせ。 de-/serializersとの残りのリンクのようなSth。
どちらの方法でも-なぜ本当に必要なのですか?描画のためだけですか?努力する価値はありません。ページネーションはありませんが、うまくいけばストリーミング(ライブ更新?)できるが「気分が悪い」。