эта проблема с n+1 запросов. И одно из решение этой проблемы было реализовано в рамках не самой технологии, а отдельной библиотекой, что скорей всего говорит о том, что для них это не проблема?
Потому что запросы к базе изначально атомарные
Хорошо поставленный вопрос и содержит уже сам в себе ответ. В реализации самой спецификации GraphQL нет проблемы N+1. Графкуэль берет и выполняет запрос который прислал клиент. Проблема N+1 возникает непосредственно в вашем коде резолверов. В коде который вы написали сами. Если чутка “умнее” написать свой код, например с помощью DataLoader’а, то проблема решается. Описание проблемы N+1: эта проблема возникает, когда вы запрашиваете Список элементов и к каждому элементу этого списка запрашиваете связные ресурсы. В графкуэль-запросе попросили 100 статей, и к каждой статье запросили данные автора. Чтобы выполнить такой запрос клиента, необходимо (вариант ИЗИ): - получить 100 статей из базы (1 запрос в БД) - из каждой статьи взять id автора - сделать отдельный запрос на получение данных автора по полученному id шагом выше (N запросов в БД) Как такая проблема решается с DataLoader’ом (вариант НОРМ) - получить 100 статей из базы (1 запрос в БД) - из каждой статьи взять id автора - положили id автора в DataLoader, который возвращает promise (N операций отложи ID) - на следующий тик eventLoop’а выполнить один запрос findMany (вместо findById) по всем собранным id-шникам авторов (1 запрос в БД) - полученных авторов отдать в DataLoader в правильном порядке, которые разрезолвит промисы на 3-ем шаге. Как решается проблема по другому без DataLoader (вариант ХАРД) - Вы получили запрос от клиента, и можете в резолвере на первом (верхнем) уровне получить из 4-го аргумента info AST-дерово запроса. Согласно этого AST’a сразу сделать большой запрос с JOIN’ами в базу, и полченные данные трансформировать в форму которую запросил клиент. Т.е. у вас резолверы только на верхнем уровне. По такому пути, Hasura работает. —— Как же мы незаметно попадаем на проблему N+1? - 1) Решили описать тип Post - 2) Потом описали тип Author - 3) Описываем связь 1 к 1 - добавляем поле Post.author и в нем просто по authorId дергаем запись автора из базы через findById. …некоторое время спустя… - 4) А давай-ка прикртим в Query.postMany поле, которое будет возвращать список Post. …бац, и у вас незаметно появилась проблема N+1… На шаге 4 вы ничего страшного не сделали, просто запросили список статей. Корень проблемы в шаге 3 - когда вы его реализовывали, вы даже и не думали что создавая такую простую связь по id через findById, она может быть использована как то “неоптимально”. А именно – будет запрошана кучу раз в одном запросе от клиента. Итог прост: если вы “связываете” между собой два типа (Post и Author), то старайтесь сделать резолвер подготовленным к множественному вызову в рамках одного запроса. например как написано здесь: https://github.com/nodkz/conf-talks/tree/master/articles/graphql/dataloader —— Ах, да. Проблема N+1 также существует и с REST API, просто она менее очевидна, но смысл тот же. В тупую дернули список статей, а потом дергаем описание авторов по одному (нагрузка на бэк такая же как и с GraphQL). Как решаете эту проблему с REST API: - новая агрегационная ручка (похоже на вариант ХАРД выше) - на клиенте собираем список авторов в какой-нить масивчик, а потом через метод findMany дергаем всех за один запрос (похоже на вариант НОРМ выше) - нифига не делаем, бэкендеры там все кэшируют (похоже на вариант ИЗИ выше)
Обсуждают сегодня