Slide 1

Slide 1 text

Introduction to Database Connection Management Patterns in TypeScript 2024/5/11 TSKaigi @sugar235711

Slide 2

Slide 2 text

2 sugar(sugar-cat) 所属: Cyber Agent/AIShift バックエンドエンジニア パフォーマンスチューニングとかセキュリティの話が好きです 自己紹介

Slide 3

Slide 3 text

3 Agenda 1. usingについて 2. DatabaseのConnection管理について 2.1. クエリビルダーとPool 2.2. Poolを扱う際の注意点 2.3. 実装方法(try-finally) 2.4. 実装方法(using) 3. まとめ

Slide 4

Slide 4 text

4 TypeScript 5.2で追加されたExplicit Resource Management(明示的リソース 管理)機能 ● usingというキーワードとともに宣言された変数は、スコープの終わりに Symbol.disposeメソッドが自動的に呼び出される 1. usingについて https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management

Slide 5

Slide 5 text

5 TypeScript 5.2で追加されたExplicit Resource Management(明示的リソース 管理)機能 ● usingというキーワードとともに宣言された変数は、スコープの終わりに Symbol.disposeメソッドが自動的に呼び出される 1. usingについて https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management ①Disposableを実装

Slide 6

Slide 6 text

6 TypeScript 5.2で追加されたExplicit Resource Management(明示的リソース 管理)機能 ● usingというキーワードとともに宣言された変数は、スコープの終わりに Symbol.disposeメソッドが自動的に呼び出される 1. usingについて https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management ①Disposableを実装 ②usingで変数をBinding

Slide 7

Slide 7 text

7 TypeScript 5.2で追加されたExplicit Resource Management(明示的リソース 管理)機能 ● usingというキーワードとともに宣言された変数は、スコープの終わりに Symbol.disposeメソッドが自動的に呼び出される 1. usingについて https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management ①Disposableを実装 ②usingで変数をBinding ③スコープを抜ける際にSymbol.disposeの処理が 実行される ※Processのkillでは実行されない

Slide 8

Slide 8 text

8 TypeScript 5.2で追加されたExplicit Resource Management(明示的リソース 管理)機能 ● usingというキーワードとともに宣言された変数は、スコープの終わりに Symbol.disposeメソッドが自動的に呼び出される 1. usingについて https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management ①Disposableを実装 ②usingで変数をBinding ③スコープを抜ける際にSymbol.disposeの処理が 実行される ※Processのkillでは実行されない ➔ try-finally blockの代替 ・宣言的なリソース解放 ・統一的なリソース解放のためのIFの提供

Slide 9

Slide 9 text

9 非同期処理に対応したSymbol.asyncDispose、Disposableインターフェースを 実装せずともCleanupが行えるDisposableStackもある 1. usingについて https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management

Slide 10

Slide 10 text

10 非同期処理に対応したSymbol.asyncDispose、Disposableインターフェースを 実装せずともCleanupが行えるDisposableStackもある 1. usingについて 各ランタイム上でのusing互換のAPIが実装されている ※DisposableStackはそのまま使えない(今のところ) Node.js: v20.4.0 https://nodejs.org/en/blog/release/v20.4.0#support-to-the-explicit-resource-manage ment-proposal Deno: v1.38 https://deno.com/blog/v1.38#using-with-deno-apis Bun: v1.0.23 https://bun.sh/blog/bun-v1.0.23#resource-management-is-now-supported Workers https://developers.cloudflare.com/workers/runtime-apis/rpc/lifecycle/#how-to-use-the -using-declaration-in-your-worker ※V8 エンジン側でネイティブサポートされてないので、 Wranglerのプレリリース 版を使用する https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management

Slide 11

Slide 11 text

11 昨今TypeScriptを使ったバックエンド開発のライブラリ選定は2パターン ● オールインワン系 ○ DBMSに依存しないAPI/Migration機構/Pooling (e.g. Prisma) ● 組み合わせ ● クエリビルダーとしての機能がメインなORM(軽量) (e.g. Drizzle, Kysely, sqlc) ● DBMSごとコネクションプールを管理するDriver (e.g. node-postgres) 2.1. クエリビルダーとコネクションプール 2. DatabaseのConnection管理について

Slide 12

Slide 12 text

12 昨今TypeScriptを使ったバックエンド開発のライブラリ選定は2パターン ● オールインワン系 ○ DBMSに依存しないAPI/Migration機構/Pooling (e.g. Prisma) ● 組み合わせ ● クエリビルダーとしての機能がメインなORM(軽量) (e.g. Drizzle, Kysely, sqlc) ● DBMSごとコネクションプールを管理するDriver (e.g. node-postgres) 2.1. クエリビルダーとコネクションプール 2. DatabaseのConnection管理について

Slide 13

Slide 13 text

13 基本的にはDB(Schema)ごとにPoolは1つ(シングルトンで扱う) 1Pool内でトランザクションごとにコネクションを発行/解放して使い回す 2.2. コネクションプールを扱う際の注意点 2. DatabaseのConnection管理について

Slide 14

Slide 14 text

14 高階関数で実行したいクエリを渡して、try-finallyでコネクションとロールバックの 制御をする 2.3. 実装方法(try-finally) 2. DatabaseのConnection管理について

Slide 15

Slide 15 text

15 コネクションはfinallyで補足しリリースする ➔ クエリの成功・失敗にかかわらずコネクションをリリースできる 2.3. 実装方法(try-finally) 2. DatabaseのConnection管理について

Slide 16

Slide 16 text

16 コネクションはfinallyで補足しリリースする ➔ クエリの成功・失敗にかかわらずコネクションをリリースできる 2.3. 実装方法(try-finally) 2. DatabaseのConnection管理について [問題点] conn.release()でエラーが起きた場合、 新たにエラーがthrowされ、クエリ実行時のエ ラーが上書きされてしまう

Slide 17

Slide 17 text

17 Drizzle ORMのpg-coreでは内部のPoolの管理にtry-finallyが使用されている 2.3. 実装方法(try-finally) ※余談 2. DatabaseのConnection管理について https://github.com/drizzle-team/drizzle-orm/blob/a78eefe08e127922565486143e0150a718b27e8a/drizzle-orm/src/node-postgres/session.ts#L145

Slide 18

Slide 18 text

18 処理の流れが見づらい場合はResult型を定義し、一連の処理を扱うとネストが減 り見通しが良い 2.3. 実装方法(Resultを用いた実例) ※余談 2. DatabaseのConnection管理について

Slide 19

Slide 19 text

19 finally部分の処理をDisposableStackのdefer内に移動 2.4. 実装方法(using: finallyをDisposableStackに置き換え) 2. DatabaseのConnection管理について Txのスコープを外れる際にコネクションが解放される

Slide 20

Slide 20 text

20 finally部分の処理をDisposableStackのdefer内に移動 2.4. 実装方法(using: TransactionのfinallyをDisposableStackに置き換え) 2. DatabaseのConnection管理について Txのスコープを外れる際にコネクションが解放される クエリ実行時かつcleaup内のreleaseでエラーになっ た場合、エラーは上書きされずSuppressedErrorとし てthrowされる

Slide 21

Slide 21 text

21 finally部分の処理をDisposableStackのdefer内に移動 2.4. 実装方法(using: TransactionのfinallyをDisposableStackに置き換え) 2. DatabaseのConnection管理について Txのスコープを外れる際にコネクションが解放される クエリ実行時かつcleaup内のreleaseでエラーになっ た場合、エラーは上書きされずSuppressedErrorとし てthrowされる SuppressedErrorの各プロパティに2種類の エラーが捕捉される:最後のthrowと直近のthrow) error: conn.release()の解放エラー suppressed: op.getError()のエラー →Txの可読性・エラートレースを改善可能 ※Pool側にDisposableの実装を行っても同等の挙動を実現できます

Slide 22

Slide 22 text

22 ● 高階関数でクエリを実行するとTransactionの流れが追いやすい ● usingとを使用することでコネクションの解放処理の可読性を向上できるよう になる ● Suppressed Errorによって複数エラーの識別が可能になる Special Thanks🎉 @brn227 @KK_sep_TT @__yoshi_dev 3. まとめ