Slide 1

Slide 1 text

PRISMA2 WITH GRAPHQL @joe_re

Slide 2

Slide 2 text

INTRODUCTION WHO AM I? ▸ twitter: @joe_re ▸ github: @joe-re ▸ GraphQL Tokyoのオーガナイザの1⼈です ▸ オンラインレッスン/ミーティングのサービスを作っています(@classdo) ▸ Prisma1は結構⻑く使ってきました (Graphcool時代から数えると2年半ぐらい)

Slide 3

Slide 3 text

PRISMA1 AND PRISMA2 DIFFERENCE OF PRISMA1 AND PRISMA2 ▸ Prisma1はGraphQLのバックエンドサーバ(Prisma Server)を提供し、 http経由でDBのマイグレーションの指示や、GraphQLクエリとDBクエリの変 換、データの更新や取得を⾏う ▸ Prisma2ではPrisma Serverは撤廃され、アプリケーションコードから直接Prisma Engine(Rust製)というバイナリのAPIを叩いてデータの取得やマイグレーション を⾏う ▸ クライアントのAPIインターフェイスなどは受け継いでいるものの、 アーキテクチャからして全くの別物

Slide 4

Slide 4 text

* Prisma2のプレビューのアナウンスメントブログより抜粋 https://www.prisma.io/blog/announcing-prisma-2-zq1s745db8i5 PRISMA1 AND PRISMA2

Slide 5

Slide 5 text

WHAT IS PRISMA2 OVERVIEW OF PRISMA2 ▸ Prisma2ではAPIサーバ側に直接DB接続を持つ ▸ イメージ的にはよりORMに近い動きになる (ただしPrismaは独⾃DSLに基づいたクライアントのオートジェネレーションや マイグレーションを提供することから⾃身のことをORMではなく database toolkitと位置づけしている)

Slide 6

Slide 6 text

WHAT IS PRISMA2 OVERVIEW OF PRISMA2 アプリケーションサイド Prisma Client (旧Photon) クライアントのオートジェネレーションツール Prisma Migrate (旧Lift) 独⾃DSL(.prisma)に基づいたマイグレーション ツール Prisma CLI それぞれのツールの呼び出しのコマンドの提供 Prisma Studio データベースのビジュアルエディタ クエリエンジン Prisma Engine データベースへのクエリエンジン(Rust製) バイナリとして提供されて、アプリケーション サーバに配置される *Prismaの公式ドキュメント、Query Engineのページより抜粋 https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-client/query-engine

Slide 7

Slide 7 text

WHAT IS PRISMA2 Q:あれ、GraphQL関係なくね? ▸ はい、ありません

Slide 8

Slide 8 text

WHAT IS PRISMA2 ▸ Prisma2はPrisma1からクエリエンジンとクライアント(ORM)やマイグレーション やその周辺ツール部分をブラッシュアップしたプロダクト ▸ 基本的にGraphQLとのつながりはない (RestAPIサーバでもCLIツールでもなんで も使える) ▸ GraphQLの実装はNexusを使うことが推奨されている

Slide 9

Slide 9 text

WHAT IS NEXUS WHAT IS NEXUS ▸ GraphQLのアプリケーションサーバを開発するTypeScriptのフレームワーク ▸ レポジトリはPrismaのオーガナイゼーション配下にはないけど、 ドキュメントに何度も登場したりPrismaの開発者がコントリビュートしていたり ほぼ公式の位置づけ ▸ Schema-First開発をシンプルにして、その利便性とCode-First開発の保守性、 開発の素早さとを両⽴させることを⽬指している

Slide 10

Slide 10 text

WHAT IS NEXUS DEVELOPMENT GRAPHQL SERVER ON NEXUS ▸ Code-Firstのアプローチで、先にResolverを記述する (graphql-jsの上で開発していて、⾒た⽬はSDLっぽくなる) ▸ Resolverの記述に伴い、開発サーバがリアルタイムにコードをビルドして、 GraphQLスキーマファイルとそれに対応した型ファイルの⽣成を⾏う ▸ 全てをリアルタイムにビルドする & 完全な型付けを提供することで、 ⼀般的なSchema-Firstにおける、GraphQLスキーマの記述 -> コードジェネレー ションの⼿間を省きつつその利点を得る

Slide 11

Slide 11 text

WHAT IS NEXUS DEVELOPMENT GRAPHQL SERVER ON NEXUS api/Post.ts (Resolver) app.graphql node_modules/@type/typegen-nexus/index.d.ts 1. 先にResolverを記述 2. 開発サーバが変更を検知 3-1. GraphQLスキーマファイルの⽣成 3-2. Resolverの型ファイルを⽣成 (node_modules/@types以下に⽣成される)

Slide 12

Slide 12 text

DEMO

Slide 13

Slide 13 text

WHAT IS NEXUS THE IMPRESSION OF NEXUS ▸ ⼩さい範囲で試している範囲では快適な体験 ▸ 個⼈的にはSchema-First寄りの意⾒を持っていたけど、 このアプローチが上⼿くスケールするならあり (ただし今までPrismaは途中で開発をやめたプロダクトが少なからず あるので少し⼼配) ▸ プロダクトが⼤きくなった時のコードジェネレーションの速度が気になる

Slide 14

Slide 14 text

時間があればちょっとだけ クエリオプティマイズの話をします

Slide 15

Slide 15 text

GraphQLでN+1起きがち問題ありますよね??

Slide 16

Slide 16 text

こんなの ユーザ全件を取得するクエリ定義 ユーザのタイプ定義 (Type Resolver) ユーザ1件に対して1件のProfileを取得する

Slide 17

Slide 17 text

PRISMA1ではどう解決されていたか ▸ Prisma1ではPrisma Serverへ⾶ぶリクエストがまとめられて、⼀気にサーバ側で 解決される https://github.com/prisma-labs/http-link-dataloader ▸ ⼀⾒N+1が起きそうなコードを書いても、 ある程度のところまでは⾃動でオプティマイズされる ( 深刻なパフォーマンスの問題が出ればもちろん真剣に取り組む必要はある) ▸ Prisma1からPrisma2へ移⾏を考える上で検証は避けては通れないところ

Slide 18

Slide 18 text

試してみた

Slide 19

Slide 19 text

結果 ▸ 1 つ⽬: ユーザ全体を取得する(params: [-1, 0]) SELECT `dev`.`User`.`id`, `dev`.`User`.`email`, `dev`.`User`.`name` FROM `dev`.`User` WHERE 1=1 LIMIT ? OFFSET ? ▸ 2つ⽬-4つ⽬: ユーザ単位でポストの全体を取得する (params: [userId, -1, 0]) SELECT `dev`.`Post`.`id`, `dev`.`Post`.`authorId`, `dev`.`Post`.`content`, `dev`.`Post`.`published`, `dev`.`Post`.`title` FROM `dev`.`Post` WHERE (`dev`.`Post`.`id`) IN (SELECT `t0`.`id` FROM `dev`.`Post` AS `t0` INNER JOIN `dev`.`User` AS `j0` ON (`j0`.`id`) = (`t0`.`authorId`) WHERE `j0`.`id` = ?) LIMIT ? OFFSET ? ▸ 5つ⽬: Profile取得対象のユーザの存在を確かめる(?) (params: [1, 2, 3, -1, 0]) SELECT `dev`.`User`.`id` FROM `dev`.`User` WHERE `dev`.`User`.`id` IN (?,?,?) LIMIT ? OFFSET ? ▸ 6つ⽬: Profileの取得(params: [1, 2, 3, -1, 0]) SELECT `dev`.`Profile`.`id`, `dev`.`Profile`.`bio`, `dev`.`Profile`.`userId` FROM `dev`.`Profile` WHERE `dev`.`Profile`.`userId` IN (?,?,?) LIMIT ? OFFSET ? まとめられてる!

Slide 20

Slide 20 text

気になったのでコードを追ってみたところ、現状はfindOneのクエリはまとめられるようです ▸ Client側のリクエストのバッチ送信処理 https://github.com/prisma/prisma/blob/2.5.1/src/packages/client/src/runtime/Dataloader.ts#L31 ▸ クエリエンジン側のオプティマイゼーションの判定処理 (同じファイルにオプティマイゼーションの処理も ある) https://github.com/prisma/prisma-engines/blob/2.6.0-dev.44/query-engine/core/src/query_document/ mod.rs#L61 ▸ GraphQLのResolverで最もN+1が起きやすいコードはこれだと思うので、ここがオプティマイズされるのは 嬉しい ▸ オプティマイズ効かせるのに少しコツがいりそうなので、このあたりもドキュメントになって欲しい気持ち

Slide 21

Slide 21 text

THANK YOU!