Apollo Server Низкая производительность при разрешении больших данных - PullRequest
5 голосов
/ 02 июня 2019

При разрешении больших данных я замечаю очень низкую производительность с момента возврата результата от моего распознавателя клиенту.

Я предполагаю, apollo-server перебирает мой результат и проверяет типы ...в любом случае операция занимает слишком много времени.

В моем продукте мне нужно возвращать большое количество данных одновременно, так как они используются одновременно для рисования диаграммы в пользовательском интерфейсе.Для меня нет возможности разбивки на страницы, где я могу нарезать данные.

Я подозреваю, что медлительность исходит от apollo-server, а не от создания объекта моего резольвера.Для создания объекта, его быстрого, а не горлышка бутылки, требуется решатель.

Более поздние операции, выполняемые apollo-server, которые я не знаю, как измерить, занимают много времени.

Теперь у меня есть версия, где я возвращаю пользовательский скалярный тип JSON, ответ гораздо быстрее.Но я действительно предпочитаю возвращать мой тип Series.

Я измеряю разницу между двумя типами (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.4s


Стек:

"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}`);
});


Запрос gql для игровой площадки (для типаSeries):

query {
  complex {
    data {
      name
      values {
        name
        value
      }
    }
    keys {
      name
      value
    }
  }
}

Запрос gql для игровой площадки (для пользовательского скалярного типа JSON):

query {
  complex
}

Вот рабочий пример:

https://codesandbox.io/s/apollo-server-performance-issue-i7fk7

Любые предложения / идеи будут высоко оценены!

Ответы [ 2 ]

3 голосов
/ 13 июня 2019

Есть связанный открытый вопрос здесь . Ли Байрон подвел итог довольно неплохо:

Я думаю, что TL; DR этой проблемы заключается в том, что GraphQL имеет некоторые накладные расходы, и что сокращение этих накладных расходов нетривиально, и их полное удаление может быть не вариант. В конечном счете, GraphQL.js по-прежнему отвечает за обеспечение гарантий API границ в отношении формы и типа возвращаемых данных и по своей конструкции не доверяет базовым системам. Другими словами, GraphQL.js выполняет проверку типов и подвыбор во время выполнения, и это требует определенных затрат.

Преимущества, которые предлагает GraphQL (проверка, дополнительный выбор и т. Д.), Неизбежно повлекут за собой некоторые накладные расходы, поскольку требуют дополнительной обработки возвращаемых вами данных. И, к сожалению, эти накладные расходы масштабируются с размером данных. Я полагаю, что если бы вы реализовали конечную точку REST, которая поддерживала частичные ответы и выполняла проверку ответов, используя что-то вроде Swagger или Joi, вы бы столкнулись с подобной проблемой.

Ошибка «куча нехватки памяти» означает именно то, что она говорит - у вас заканчивается память в куче. Вы можете попытаться облегчить это, вручную увеличивая предел .

Как правило, большие наборы данных, подобные этому, должны быть разбиты путем внедрения нумерации страниц. Если это не вариант, то следующим подходом будет использование пользовательского скаляра . Самым большим недостатком этого подхода является то, что клиенты, использующие ваш API, не смогут запрашивать определенные поля внутри возвращаемого вами объекта JSON. Помимо исправления GraphQL.js , на самом деле нет другой альтернативы, чтобы ускорить ответы и уменьшить использование памяти.

0 голосов
/ 04 июня 2019

Сводка комментариев

Эта структура данных / типы:

  • не являются отдельными объектами;
  • просто серией [groupped]данные;
  • не нуждаются в нормализации;
  • не будет должным образом нормализоваться в кэше apollo (без полей id);

Таким образом этот набор данных не предназначен для GraphQL. Конечно, GraphQL все еще может использоваться для извлечения этих данных, но анализ / сопоставление типов должно быть отключено.

Решением может быть использование пользовательских скалярных типов (graphql-type-json).Если вам нужно гибридное решение - вы можете набрать Group.values как json (вместо целого Series).Группы по-прежнему должны иметь поле id, если вы хотите использовать нормализованный кеш [доступ].

Альтернатива

Вы можете использовать apollo-link-rest для выборки 'pure'json-данные (файл), оставляя тип анализа / сопоставления только для клиентской стороны.

Более продвинутая альтернатива

Если вы хотите использовать одну конечную точку graphql ... напишите собственную ссылку - используйте директивы - 'спросите JSON, наберите '- смесь двух выше.Sth, как в покое ссылка с де- / сериализаторами.


В обеих альтернативах - зачем вам это действительно нужно? Только для рисования?Не стоит усилий.Нет нумерации страниц, но, надеюсь, потоковое (живые обновления?) ... нет курсоров ... загрузить больше (подписки / опросы) с помощью ... последнего обновления?Выполнимо, но «не чувствую себя хорошо».

...