GraphQLやるならDataloaderを使おう

24.1K Views

April 05, 23

スライド概要

2023年3月19日(土) に開催した「YAPC::Kyoto」の前夜祭で 技術統括部エンジニアリング室 インターン の 葉狩 勇人 が登壇した資料です。

profile-image

DeNA が社会の技術向上に貢献するため、業務で得た知見を積極的に外部に発信する、DeNA 公式のアカウントです。DeNA エンジニアの登壇資料をお届けします。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

GraphQLやるならDataloaderを 使おう 葉狩勇人 技術統括部エンジニアリング室インターン 株式会社ディー・エヌ・エー © DeNA Co., Ltd. 1

2.

自己紹介 葉狩 勇人(gari8) DeNA 技術統括部エンジニアリング室 北部方面第7師団所属の陸上自衛官として5年間勤務 していたが、一年発起して文系学部へ大学受験。 現在はDeNAで23卒内定者インターンとして日比谷 音楽祭アプリの開発チームで勤務。 @gari8 @HAGARI06 © DeNA Co., Ltd. 2

3.

日比谷音楽祭についてもっと知りたい方は © DeNA Co., Ltd. 日比谷音楽祭 2023 🔎 3

4.

GraphQLの採用 © DeNA Co., Ltd. 4

5.

2 GraphQLとは? ● 型システムを使ってクエリを実行するた type Team { name: String! members(role: Role): [Member!] } めのサーバーサイドランタイム ● チームとメンバーとその役割を 右のように表せる type Member { name: String! role: Role! team: Team! } enum Role { SUPERVISOR STAFF PLAYER } © DeNA Co., Ltd. 5

6.

3 GraphQLとは? { "data": { "teams": [ { "name": "A高校サッカークラブ ", "members": [ { "name": "田中太郎", "role": "PLAYER" } ... ] } ... ] } teamsというQueryを定義すると、以下のよう なリクエストができ、右のような結果となる。 query { teams { name members(role: PLAYER) { name role } } } © DeNA Co., Ltd. } 6

7.

導入時の失敗 © DeNA Co., Ltd. 7

8.

GraphQLのクエリ処理上でN+1問題が起こる resolverでの処理(必要なタイプの処理のみ) Teamに関する処理 query { teams { name members { name role } } } © DeNA Co., Ltd. SELECT * FROM teams →N件のレコード取得 + Teamに紐づくMembersに関する処理 SELECT * FROM members WHERE team_id = 1 SELECT * FROM members WHERE team_id = 2 SELECT * FROM members WHERE team_id = 3 →N回のSQL発行 =N+1回のSQL 8

9.

N+1問題への対応 resolverでの処理(全ての処理を一つのタイプで) TeamとMembersをJOINして全て取得する処理 query { teams { name members { name role } } } © DeNA Co., Ltd. SELECT * FROM teams LEFT JOIN members ON teams.id = members.team_id; →1回のSQL発行 ただし、ネストしたいタイプが増えると 必要なくてもJOINするはめに、、 9

10.
[beta]
N+1問題をJOINで解決した結果起きてしまった問題
●

クライアント側から、ネストして取得したいタイプというのが開発が進むたびに増
えていき、JOINするテーブルが増えてしまった…→SlowQueryの頻発

● JOINによって永続化層でのロジックが肥大化してしまい→コードの可読性の低下
// DBlxを使用した例(たくさん JOINしてなにやらカオスに …)

$schema->resultset('teams')->search(
{ join

=> [qw/ members members_child_table members_grand_child_table ...(略)... /],

'+select' => [ qw/ teams.name members.name ...(略)... /]
}
);

© DeNA Co., Ltd.

10

11.

Dataloaderの登場 © DeNA Co., Ltd. 11

12.

Dataloaderとは ● ある短い時間で複数のリクエストを集積して、データベースやマイクロサービスなどに 単一リクエストとして送信する “a naive GraphQL service could be very "chatty" or repeatedly load data from your databases. This is commonly solved by a batching technique, where multiple requests for data from a backend are collected over a short period of time and then dispatched in a single request to an underlying database or microservice by using a tool like Facebook's DataLoader.” (出典:GraphQL Best Practicies | GraphQL) © DeNA Co., Ltd. 12

13.

Dataloader導入の効果 バッチ処理によってGraphQLにおけるN+1問題を解決 ネストするタイプを全てJOINする必要がなくなる →コードの可読性の向上 →SlowQueryの撃滅 © DeNA Co., Ltd. 13

14.

Dataloaderは何をしてるの? graphql/dataloaderのソースコード読んでみた © DeNA Co., Ltd. 14

15.

結果→GraphQLとDataloaderの処理手順がわかった! STEP① 親タイプの処理 © DeNA Co., Ltd. STEP② 子タイプから Keyを集積 STEP③ 集めたKeyを 利用した バッチ処理 STEP④ Keyに基づいて レスポンスを 処理 15

16.

今回使用するGraphQLリクエストとレスポンス { query { teams { name members { name role } } } © DeNA Co., Ltd. "data": { "teams": [ { "name": "A高校サッカークラブ ", "members": [ { "name": "田中太郎", "role": "PLAYER" } ... ] } ... ] } } 16

17.

親タイプの処理 1 middlewareなど ①TeamResolverはDBに対してSQLを発 行して、N件のレコードを取得する バッチ関数 ②N件のTeamタイプからそれぞれの子 タイプであるMemberに対して親の情報 が渡される Resolver ① Team DB ② Member Dataloaderの処理 GraphQLの処理 © DeNA Co., Ltd. 17

18.

子タイプからKeyを集積 2 middlewareなど ①Memberは親であるTeamから受け 取った情報の中から適当なものをKeyと いう番号札のようなものとして、バッ チ関数宛に送信する バッチ関数 graphql/dataloaderではloadというメ ソッドで実装されている Resolver ① Team DB Member Dataloaderの処理 GraphQLの処理 © DeNA Co., Ltd. 18

19.

集積したKeyを利用したバッチ処理 3 middlewareなど ① ①Memberから預かっているN件の番号 札を元に(WHERE INなどの)SQLを発 行する バッチ関数 ② ②レコードが一気に返ってくるので受 け取る Resolver Team DB Member Dataloaderの処理 GraphQLの処理 © DeNA Co., Ltd. 19

20.

Keyに基づいてレスポンスを処理 4 middlewareなど ①番号札を元にレコードをMemberに返 していく バッチ関数 ③ ②Memberは直接DBを叩くことなくレ コードを取得できるので、Teamの情報 と合わせてGraphQLに渡す Resolver ③GraphQLがよしなに返してくれる ① Team DB 過度なJOINの必要なし! 本来発生するはずだった MemberでのSQL発行を N→1にしてくれる ② Member Dataloaderの処理 GraphQLの処理 © DeNA Co., Ltd. 20

21.

GraphQLの特性を壊さず、 バッチ処理というイメージしやすいロジックで解決 している! いぶし銀という感じでステキ! © DeNA Co., Ltd. 21

22.

まとめ ● DataloaderはSQL発行を最小限にとどめてくれる、主婦も驚きの倹約 家!⇦ NICE! ● 非同期を上手く使うことができれば、スクラッチでも全然実装出来そ うなほどシンプルな構造!⇦ NICE! ● OSSを利用する際はソースコードを読んでみると解像度が上がって実 装のイメージが湧く! ● GraphQL使うならDataloaderを使おう! © DeNA Co., Ltd. 22

23.

ご傾聴ありがとうございました! © DeNA Co., Ltd. 23

24.

DeNAについてもっと知りたい方は © DeNA Co., Ltd. DeNA Engineering 🔎 24