Lors de la résolution de données volumineuses, je constate une performance très lente, à partir du moment où je renvoie le résultat de mon résolveur au client.
Je suppose apollo-server
répète mon résultat et vérifie les types ... de toute façon, l'opération prend trop de temps.
Dans mon produit, je dois renvoyer une grande quantité de données en une seule fois, car elles sont utilisées, en une seule fois, pour dessiner un graphique dans l'interface utilisateur. Il n'y a pas d'option de pagination pour moi où je peux découper les données.
Je soupçonne la lenteur venant de apollo-server
et non ma création d'objet résolveur.
Notez que j'enregistre le temps nécessaire au résolveur pour créer l'objet, son rapide et non le goulot de la bouteille.
Opérations ultérieures effectuées par apollo-server
, que je ne sais pas mesurer, prend beaucoup de temps.
Maintenant, j'ai une version, où je renvoie un JSON de type scalaire personnalisé, la réponse est beaucoup plus rapide. Mais je préfère vraiment retourner mon type Series
.
Je mesure la différence entre les deux types (Series
et JSON
) en regardant le panneau réseau.
lorsque AMOUNT est défini sur 500 et que le type est Series
, cela prend ~ 1,5s (c'est-à-dire secondes)
lorsque AMOUNT est défini sur 500 et que le type est JSON
, cela prend ~ 150 ms (rapide!)
lorsque AMOUNT est défini sur 1000 et que le type est Series
, c'est très lent ...
lorsque AMOUNT est défini sur 10000 et que le type est Series
, je reçois de la mémoire JavaScript (ce qui est malheureusement ce que nous rencontrons dans notre produit)
J'ai aussi comparé apollo-server
performance à express-graphql
, ce dernier fonctionne plus rapidement, mais pas aussi rapidement que le retour d'un JSON scalaire personnalisé.
lorsque AMOUNT est défini sur 500, apollo-server
, le réseau prend 1,5s
lorsque AMOUNT est défini sur 500, express-graphql
, le réseau prend 800 ms
lorsque AMOUNT est défini sur 1000, apollo-server
, le réseau prend 5,4 secondes
lorsque AMOUNT est défini sur 1000, express-graphql
, le réseau prend 3,4 secondes
La pile:
"dependencies": {
"apollo-server": "^2.6.1",
"graphql": "^14.3.1",
"graphql-type-json": "^0.3.0",
"lodash": "^4.17.11"
}
Le code:
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}`);
});
La requête gql pour le terrain de jeu (pour le type Series):
query {
complex {
data {
name
values {
name
value
}
}
keys {
name
value
}
}
}
La requête gql pour le Playground (pour le type scalaire personnalisé JSON):
query {
complex
}
Voici un exemple de travail:
https://codesandbox.io/s/apollo-server-performance-issue-i7fk7
Toutes les pistes/idées seraient très appréciées!
Résumé des commentaires
Cette structure/types de données:
id
);De cette façon cet ensemble de données n'est pas le graphQL a été conçu pour. Bien sûr, graphQL peut toujours être utilisé pour récupérer ces données mais l'analyse de type/correspondance doit être désactivée.
Utilisation de types scalaires personnalisés (graphql-type-json
) peut être une solution. Si vous avez besoin d'une solution hybride, vous pouvez taper Group.values
comme json (à la place entier Series
). Les groupes doivent toujours avoir un champ id
si vous souhaitez utiliser le cache normalisé [accès].
Vous pouvez utiliser apollo-link-rest
pour récupérer les données json "pures" (fichier) en laissant le type analyse/correspondance uniquement côté client.
Si vous voulez utiliser un point de terminaison graphql ... écrivez votre propre lien - utilisez les directives - 'demandez json, faites-vous taper' - mélange de deux ci-dessus. Sth comme dans le lien de repos avec de-/sérialiseurs.
Dans les deux alternatives - pourquoi en avez-vous vraiment besoin? Juste pour dessiner? Ne vaut pas l'effort. Pas de pagination mais j'espère en streaming (mises à jour en direct?) ... pas de curseurs ... charger plus (abonnements/sondages) d'ici ... la dernière mise à jour? Faisable mais "ne se sent pas bien".