Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

接続のライフサイクル

最終更新 Markdown で表示Agent セットアップ

Workers、Hyperdrive、オリジンデータベースの間で接続がどのように管理されるかを理解することは、Hyperdrive で効率的なアプリケーションを構築するために重要です。

Hyperdrive は Cloudflare のネットワーク内でデータベースへのコネクションプールを維持します。これにより、クエリを送る前に必要な 7 回のラウンドトリップ(TCP ハンドシェイク 1 回、TLS ネゴシエーション 3 回、データベース認証 3 回)を削減できます。

接続の管理方法

Cloudflare Worker でデータベースクライアントを使う場合、接続のライフサイクルは従来のサーバー環境とは異なります。流れは次のとおりです。

Hyperdrive の接続

Hyperdrive がない場合、Worker の呼び出しごとにオリジンデータベースへ新しい接続を確立する必要があります。この接続セットアップでは、TCP ハンドシェイク、TLS ネゴシエーション、データベース認証を完了するために、インターネットを横断する複数回のラウンドトリップが必要です。クエリを実行する前に 7 回のラウンドトリップが発生し、レイテンシが増加します。

Hyperdrive は接続セットアップを 2 つに分割して解決します。高速なエッジ接続と、データベースへの最適化された経路です。

  1. エッジでの接続セットアップ: Worker コード内のデータベースドライバーが Hyperdrive インスタンスへの接続を確立します。これは Worker と同じ場所のエッジで行われるため、接続の作成は非常に高速です。このため、Hyperdrive 専用の接続文字列を使います。

  2. リージョンをまたぐ 1 回のラウンドトリップ: 認証はすでにエッジで完了しているため、Hyperdrive がデータベースへリージョンをまたいで行うラウンドトリップは 1 回だけで済みます。接続セットアップ中に発生する複数回のラウンドトリップは不要です。

  3. プールから既存接続を取得: Hyperdrive は、データベースの近くに配置されたプールから既存の接続を使います。レイテンシを最小限に抑えます。

  4. 利用可能な接続がない場合は新規作成: 必要なときは、データベースに近いリージョンから新しい接続を作成し、新規接続のレイテンシを抑えます。

  5. クエリの実行: クエリはデータベースに対して実行され、結果は Hyperdrive 経由で Worker に返されます。

  6. 接続の解放: Worker がリクエストの処理を終えると、Worker 内のデータベースクライアント接続は自動的にガベージコレクションされます。一方、Hyperdrive はオリジンデータベースへの接続をプール内で開いたままにし、次の Worker 呼び出しで再利用できるようにします。つまり、後続のリクエストでも高速なエッジ接続セットアップは行いますが、データベース近くの Hyperdrive プールにある既存接続を再利用します。

クライアント接続のクリーンアップ

Worker がリクエストの処理を終えると、データベースクライアントは自動的にガベージコレクションされ、Hyperdrive へのエッジ接続はクリーンアップされます。Hyperdrive は、再利用のためにオリジンデータベースへの基盤接続をプール内で開いたままにします。

データベースクライアントをクリーンアップするために、client.end()sql.end()connection.end()(または同等のメソッド)を呼ぶ必要はありません。Workers から Hyperdrive への接続は、リクエストまたは呼び出しが終了すると自動的にクリーンアップされます。WorkflowQueue コンシューマー の完了時、Durable Object の休止時やアイドル時の退去時も含みます。

import { Client } from "pg";

export default {
	async fetch(request, env, ctx): Promise<Response> {
		const client = new Client({
			connectionString: env.HYPERDRIVE.connectionString,
		});
		await client.connect();

		const result = await client.query("SELECT * FROM pg_tables");

		// No need to call client.end() — Hyperdrive automatically cleans
		// up the client connection when the request ends. The underlying
		// pooled connection to your origin database remains open for reuse.
		return Response.json(result.rows);
	},
} satisfies ExportedHandler<Env>;

グローバルスコープでデータベースクライアントやコネクションプールを作成しないでください。代わりに、各ハンドラーの呼び出し内で新しいクライアントを作成します。Hyperdrive のコネクションプールにより、これは高速です。

index.jsjs
import { Client } from "pg";

// 🔴 Bad: Client created in the global scope persists across requests.
// Workers do not allow I/O across request contexts, so this client
// becomes stale and subsequent queries will throw hard errors.
const globalClient = new Client({
	connectionString: env.HYPERDRIVE.connectionString,
});
await globalClient.connect();

export default {
	async fetch(request, env, ctx) {
		// ✅ Good: Client created inside the handler, scoped to this request.
		// Hyperdrive pools the underlying connection to your origin database,
		// so creating a new client per request is fast and reliable.
		const client = new Client({
			connectionString: env.HYPERDRIVE.connectionString,
		});
		await client.connect();

		const result = await client.query("SELECT * FROM pg_tables");
		return Response.json(result.rows);
	},
};
index.tsts
import { Client } from "pg";

// 🔴 Bad: Client created in the global scope persists across requests.
// Workers do not allow I/O across request contexts, so this client
// becomes stale and subsequent queries will throw hard errors.
const globalClient = new Client({
	connectionString: env.HYPERDRIVE.connectionString,
});
await globalClient.connect();

export default {
	async fetch(request, env, ctx): Promise<Response> {
		// ✅ Good: Client created inside the handler, scoped to this request.
		// Hyperdrive pools the underlying connection to your origin database,
		// so creating a new client per request is fast and reliable.
		const client = new Client({
			connectionString: env.HYPERDRIVE.connectionString,
		});
		await client.connect();

		const result = await client.query("SELECT * FROM pg_tables");
		return Response.json(result.rows);
	},
} satisfies ExportedHandler<Env>;

接続ライフサイクルの考慮事項

Durable Objects と永続接続

通常の Workers と異なり、Durable Objects は複数のリクエストにわたって状態を維持できます。Durable Object 内でデータベースクライアントを開いたままにすると、その接続は Hyperdrive のコネクションプールから割り当てられたままになります。長寿命の Durable Objects が多数同時に接続を開いたままにすると、利用可能な接続を使い果たすことがあります。

長時間実行されるトランザクション

Hyperdrive は トランザクションプーリングモード で動作し、トランザクションの間は接続を保持します。複数のクエリを含む長時間のトランザクションは、完了するまでプールから接続を保持するため、Hyperdrive の利用可能な接続をより早く使い果たすことがあります。

Workers プランに応じて、Hyperdrive 設定で利用できる接続数は 制限 を参照してください。

関連リソース

役に立ちましたか?