Hyperdrive でデータベースに接続するときに多いエラーを、切り分けてデバッグします。
Hyperdrive の設定を新規作成するとき、または既存設定の接続パラメーターを更新するとき、Hyperdrive は作成または更新の前に、バックグラウンドでデータベースへのテスト接続を行います。
Hyperdrive は空のテストクエリ(PostgreSQL では ;)も発行し、データベースへクエリを渡せることを確認します。
| エラーコード | 詳細 | 推奨する対処 |
|---|---|---|
2008 |
ホスト名が不正です。 | Hyperdrive はデータベースのホスト名を解決できませんでした。公開 DNS に存在することを確認してください。 |
2009 |
ホスト名が公開 IP アドレスに解決されない、または IP アドレスが公開アドレスではありません。 | Hyperdrive が接続できるのは公開 IP アドレスだけです。10.1.5.0 や 192.168.2.1 のようなプライベート IP アドレスは、現時点ではサポートされていません。 |
2010 |
host:port に接続できません。 | Hyperdrive はホスト名へ到達できませんでした。公開 IP アドレスに解決される公開 DNS レコードがあることを確認してください。ホスト名のスペルミスがないかも確認してください。 |
2011 |
接続が拒否されました。 | ネットワークファイアウォールまたはアクセス制御リスト(ACL)が、Hyperdrive からのリクエストを拒否している可能性が高いです。パブリックインターネットからの接続を許可してください。 |
2012 |
データベースが TLS(SSL)に対応していません。 | Hyperdrive の接続には TLS(SSL)が必要です。データベースで TLS を設定してください。 |
2013 |
データベースの認証情報が無効です。 | ユーザー名が正しい(存在している)ことと、パスワードが正しい(大文字と小文字を区別する)ことを確認してください。 |
2014 |
指定したデータベース名が存在しません。 | Hyperdrive に接続先として指定したデータベース(テーブルではなく)の名前が存在することを確認してください。 |
2015 |
一般的なエラーです。 | Hyperdrive は接続に失敗し、理由を特定できませんでした。Cloudflare が調査できるよう、サポートチケットを開いてください。 |
2016 |
テストクエリが失敗しました。 | Hyperdrive が接続に使うユーザーに、対象データベースへの読み取りと書き込みクエリの権限があることを確認してください。 |
Hyperdrive 設定の作成時にデータベースへ接続できない場合、Failed to connect to the provided database が返ることがあります。TLS(SSL)証明書の設定が誤っているときに起こり得ます。接続失敗のエラーの例を、網羅的ではない表に示します。
| エラーメッセージ | 詳細 | 推奨する対処 |
|---|---|---|
| Server return error and closed connection. | クライアント証明書の検証が有効なデータベースに接続しようとしたときに、このメッセージが出ます。 | データベースがクライアント証明書を要求する場合は、クライアント証明書 を使って Hyperdrive を設定してください。 |
| TLS handshake failed: cert validation failed. | Hyperdrive にサーバー CA 証明書を設定しており、サーバーが提示した証明書が想定する CA 証明書で署名されていないことを示します。 | Hyperdrive に正しい CA 証明書を使っていること、または正しいデータベースに接続していることを確認してください。 |
実行時に Hyperdrive がエラーを返すこともあります。初回接続のセットアップ時や、ドライバーが送ったクエリやその他のワイヤープロトコルコマンドへの応答時に起こります。
これらのエラーは ErrorResponse ワイヤープロトコルメッセージとして返されます。ほとんどのドライバーは、該当するクエリから例外を投げるか、エラーイベントを発火して処理します。
PostgreSQL が文書化している ↗ エラーメッセージコードと 1 対 1 で対応しない Hyperdrive のエラーは、エラーコード 58000 を使います。
Hyperdrive は、データベースが送った ErrorResponse ワイヤープロトコルメッセージに遭遇することもあります。可能な場合、Hyperdrive はこれらのエラーを変更せずにそのまま渡します。
| エラーメッセージ | 詳細 | 推奨する対処 |
|---|---|---|
Internal error. |
こちら側で障害が起きています。 | Hyperdrive に影響する進行中のインシデントがないか確認し、Cloudflare Support に連絡 してください。利用パターンに合う場合は、クエリの再試行も適切です。 |
Failed to acquire a connection from the pool. |
Hyperdrive はデータベースへの接続待ちでタイムアウトしたか、まったく接続できません。 | このエラーが断続的に出る場合、Worker が接続を長く開きすぎて Hyperdrive のプールが枯渇しています。原因はさまざまですが、長時間実行されるクエリやトランザクションがよくある原因です。 |
Server connection attempt failed: connection_refused |
Hyperdrive はオリジンデータベースへの新しい接続を作れません。 | ネットワークファイアウォールまたはアクセス制御リスト(ACL)が、Hyperdrive からのリクエストを拒否している可能性が高いです。パブリックインターネットからの接続を許可してください。データベースホストプロバイダーが、接続上限を超えたときに受信接続を拒否している場合もあります。 |
Hyperdrive does not currently support MySQL COM_STMT_PREPARE messages |
Hyperdrive は MySQL データベースのプリペアドステートメントに対応していません。 | MySQL クエリからプリペアドステートメントを削除してください。 |
| エラーメッセージ | 詳細 | 推奨する対処 |
|---|---|---|
Uncaught Error: No such module "node:<module>" |
Cloudflare Workers プロジェクト、またはそれがインポートするライブラリが、利用できない Node モジュールにアクセスしようとしています。 | Cloudflare Workers プロジェクトで Node.js 互換性 を有効にし、互換性を最大化してください。 |
アプリケーションがデータを書き込んだあと、後続の読み取りが古いデータを返す場合、Hyperdrive のクエリキャッシュがキャッシュ済みの読み取りクエリ結果を返している可能性があります。cacheStatus ごとの Hyperdrive メトリクスを確認し、読み取りが hit、miss、disabled、uncacheable のどれを返すかを確認してください。メトリクスと分析 を参照してください。
書き込み後の古い読み取りを解消するには、クエリキャッシュ を参照し、新しい読み取りとキャッシュ可能な読み取りの分離方法を確認してください。常に最新データが必要な読み取りには、キャッシュ無効の Hyperdrive 設定を使います。
Hyperdrive でキャッシュが有効なのにクエリがキャッシュされない場合は、次を確認してください。
-
クエリ内の STABLE または VOLATILE な PostgreSQL 関数: PostgreSQL で
STABLEまたはVOLATILEに分類される関数を含むクエリはキャッシュできません。よくある例はNOW()、CURRENT_TIMESTAMP、CURRENT_DATE、RANDOM()、LASTVAL()です。対処するには、関数呼び出しをアプリケーションコードへ移し、結果をクエリパラメーターとして渡します。たとえばWHERE created_at > NOW()の代わりに、Worker でタイムスタンプを計算し、パラメーターとして渡します:WHERE created_at > $1。キャッシュできない関数の一覧は クエリキャッシュ を参照してください。 -
SQL コメント内の関数名: Hyperdrive はテキストベースのパターンマッチで、一部のキャッシュできない関数を検出します。SQL コメント内の
NOW()のような関数名への言及でも、実際には呼び出していなくても、クエリがキャッシュ不可として扱われることがあります。コメントを含め、クエリテキストからキャッシュできない関数名への言及を削除してください。 -
ドライバーの設定: ドライバーの設定により、Hyperdrive がクエリをキャッシュできないことがあります。Postgres.js ↗ ドライバーで
prepare: false↗ を使っている場合に起こります。対処するには、prepare: trueでプリペアドステートメントを有効にします。
| エラーメッセージ | 詳細 | 推奨する対処 |
|---|---|---|
Code generation from strings disallowed for this context |
使っているデータベースドライバーが eval() コマンドを使おうとしています。Cloudflare Workers ではサポートされていません(mysql2 ドライバーでよくあります)。 |
データベースドライバーが eval() を使わないよう設定してください。mysql2 で eval() の使用を無効にする設定 を参照してください。 |
これらのエラーは、データベースクライアントまたは接続がグローバルスコープ(リクエストハンドラーの外)で作成されたとき、またはリクエストをまたいで再利用されたときに発生します。Workers では リクエストをまたいだ I/O は許可されず、前のリクエストコンテキストのデータベース接続は使えなくなります。必ず ハンドラー内でデータベースクライアントを作成 してください。
| エラーメッセージ | 詳細 | 推奨する対処 |
|---|---|---|
Disallowed operation called within global scope. Asynchronous I/O (ex: fetch() or connect()), setting a timeout, and generating random values are not allowed within global scope. |
Worker がスクリプト起動時に、リクエストハンドラーの外でデータベース接続を開く、または I/O を行おうとしています。 | データベースクライアントの作成を、fetch、queue、またはその他のハンドラー関数内へ移してください。 |
Cannot perform I/O on behalf of a different request. I/O objects (such as streams, request/response bodies, and others) created in the context of one request handler cannot be accessed from a different request's handler. |
あるリクエスト中に作成したデータベース接続またはクライアントを、後続のリクエストで再利用しています。 | グローバル変数にキャッシュせず、リクエストごとに新しいデータベースクライアントを作成してください。Hyperdrive のコネクションプーリングにより、接続開始のオーバーヘッドはすでになくなっています。 |
| エラーメッセージ | 詳細 | 推奨する対処 |
|---|---|---|
Connection terminated |
クライアントの .end() メソッドが呼ばれたか、前のリクエストの終了時に接続がクリーンアップされました。 |
前のリクエストのクライアントを再利用せず、ハンドラー内で新しい Client を作成してください。 |
Connection terminated unexpectedly |
明示的な .end() 呼び出しなしに、基盤の接続が切断されました。たとえば、前のリクエストのコンテキストがガベージコレクションされたときです。 |
リクエストごとに、ハンドラー内で新しい Client を作成してください。 |
Client has encountered a connection error and is not queryable |
接続でソケットレベルのエラーが発生しました(リクエストをまたいでクライアントを再利用するときに多いです)。 | ハンドラー内で新しい Client を作成してください。クライアントをグローバル変数に保存しないでください。 |
Client was closed and is not queryable |
すでに .end() メソッドが呼ばれたクライアントに対してクエリを実行しようとしました。 |
再利用せず、ハンドラー内で新しい Client を作成してください。 |
Cannot use a pool after calling end on the pool |
すでに終了した Pool インスタンスに対して pool.connect() が呼ばれました。 |
グローバルスコープで new Pool() を使わないでください。ハンドラー内で new Client() を作成してください。コネクションプーリングは Hyperdrive が行います。 |
Client has already been connected. You cannot reuse a client. |
前の呼び出しですでに接続済みのクライアントに対して client.connect() が呼ばれました。 |
リクエストごとに新しい Client を作成してください。node-postgres のクライアントは、一度接続すると再接続できません。 |
Postgres.js のエラーメッセージには、エラーコードと対象ホストが含まれます。エラーオブジェクトの code プロパティにエラーコードが入っています。
| エラーメッセージ | 詳細 | 推奨する対処 |
|---|---|---|
write CONNECTION_ENDED <host>:<port> |
sql.end() の呼び出し後、または前のリクエストから接続がクリーンアップされたあとに、クエリを実行しようとしました。エラーコード: CONNECTION_ENDED。 |
ハンドラー内で新しい postgres() インスタンスを作成してください。 |
write CONNECTION_DESTROYED <host>:<port> |
接続が強制終了されました。たとえば sql.end({ timeout }) の期限切れ時や、すでに接続が終了していた場合です。エラーコード: CONNECTION_DESTROYED。 |
リクエストごとに、ハンドラー内で新しい postgres() インスタンスを作成してください。 |
write CONNECTION_CLOSED <host>:<port> |
クエリがまだ待ち状態のあいだに、基盤のソケットが予期せず閉じられました。エラーコード: CONNECTION_CLOSED。 |
ハンドラー内で新しい postgres() インスタンスを作成してください。1 回のリクエスト内で発生する場合は、ネットワークの問題やクエリのタイムアウトを確認してください。 |
| エラーメッセージ | 詳細 | 推奨する対処 |
|---|---|---|
Can't add new command when connection is in closed state |
すでに閉じられている、または致命的なエラーが発生した接続に対してクエリを実行しようとしました。 | グローバルスコープの接続を再利用せず、ハンドラー内で新しい接続を作成してください。 |
Connection lost: The server closed the connection. |
サーバーによって基盤のソケットが閉じられたか、リクエスト間でガベージコレクションされました。エラーコード: PROTOCOL_CONNECTION_LOST。 |
リクエストごとに、ハンドラー内で新しい接続を作成してください。 |
Pool is closed. |
すでに閉じられたプールに対して pool.getConnection() が呼ばれました。 |
グローバルスコープで createPool() を使わないでください。ハンドラー内で新しい createConnection() を作成してください。プーリングは Hyperdrive が行います。 |
| エラーメッセージ | 詳細 | 推奨する対処 |
|---|---|---|
Cannot enqueue Query after fatal error. |
以前に致命的なエラーが発生した接続に対してクエリを実行しようとしました。エラーコード: PROTOCOL_ENQUEUE_AFTER_FATAL_ERROR。 |
グローバルスコープの接続を再利用せず、ハンドラー内で新しい接続を作成してください。 |
Cannot enqueue Query after invoking quit. |
.end() の呼び出し後に、接続へクエリを実行しようとしました。エラーコード: PROTOCOL_ENQUEUE_AFTER_QUIT。 |
リクエストごとに、ハンドラー内で新しい接続を作成してください。 |
Cannot enqueue Handshake after already enqueuing a Handshake. |
前のリクエストですでに接続済みの接続に対して .connect() が呼ばれました。エラーコード: PROTOCOL_ENQUEUE_HANDSHAKE_TWICE。 |
リクエストごとに新しい接続を作成してください。mysql の接続は、一度接続すると再接続できません。 |
クエリトラフィックをトランザクションとして書くと、性能が制限されることがあります。トランザクションでは接続をトランザクションのあいだ保持する必要があり、接続の多重化が制限されるためです。トランザクションあたりのクエリが多いと、特に影響が大きくなります。可能であれば、クエリをトランザクションで包まず、接続をより積極的に共有できるようにすることを推奨します。