web-dev-qa-db-fra.com

Apollo Server ralentit les performances lors de la résolution de données volumineuses

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!

6
sergelerner

Résumé des commentaires

Cette structure/types de données:

  • ne sont pas des entités individuelles;
  • juste une série de données [groupées];
  • n'ont pas besoin de normalisation;
  • ne sera pas normalisé correctement dans le cache apollo (pas de champs 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].

Alternative

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.

Alternative plus avancée

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".

0
xadm