Prisma + Supabase ポート6543の罠!謎のエラーとの格闘記
【結論】Prisma + Supabase 接続でハマらないために
この記事は、Prisma から Supabase のデータベースに接続する際に発生しやすい prepared statement "..." already exists
というエラーとの格闘記録です。最初に、ハマりやすいポイントとその対策をまとめます。
🚨 注意すべきこと
Prisma Client は、Supabase が提供するコネクションプーラー(PgBouncer)のトランザクションプーリングモードと相性が悪く、prepared statement "..." already exists
(PostgreSQLエラーコード 42P05
) というエラーを引き起こしやすいです。
✅ チェックすべき場所
もし上記のエラーに遭遇したら、Prisma が使用している Supabase のデータベース接続情報(DATABASE_URL
環境変数など)をよく確認してください。
-
ホスト名:
.<region>.pooler.supabase.com
のようにpooler
が含まれていませんか? -
ポート番号:
6543
になっていませんか? -
接続パラメータ: (古い形式や特殊な設定の場合)
?pgbouncer=true
のようなパラメータが付いていませんか?
これらに該当する場合、問題のトランザクションプーリングモードに接続している可能性が高いです。
🛠️ 対策
以下のいずれかの方法で接続方法を変更してください。
-
直接接続を利用する:
- Supabase ダッシュボード(Database > Connection Info)で「Use connection pooling」のチェックを外し、表示される接続文字列(ホスト名に
pooler
が含まれず、ポートが5432
)を使用します。 - Pros: 設定が簡単。
- Cons: 同時接続数の上限に達しやすくなる可能性があるため、アプリケーションの規模によっては注意が必要です。
- Supabase ダッシュボード(Database > Connection Info)で「Use connection pooling」のチェックを外し、表示される接続文字列(ホスト名に
-
Prisma Accelerate を利用する:
- Prisma 公式のコネクションプーラーサービスを導入します。
- Pros: Prisma Client との互換性が高く、接続数管理も効率化され、サーバーレス環境などにも適しています。
- Cons: Prisma Accelerate の設定と、プランによっては追加コストが発生する場合があります。
はじめに
さて、ここからは私が実際にこの問題にハマり、解決に至るまでの具体的な経緯をお話しします。
Python + Prisma + Supabase というモダンなスタックで開発を進めていました。ローカル環境では Supabase CLI を使ってコンテナを立ち上げ、Prisma Client 経由で快適にデータベース操作ができていました。「これならスムーズに本番移行できそうだ!」と思っていたのですが、現実はそんなに甘くありませんでした...。
prepared statement "s0" already exists
悪夢のはじまり:謎のエラー ローカル開発も順調に進み、いよいよ Supabase のクラウド環境に接続して動作確認をしようとしたその時、これまで見たことのないエラーが突如として現れました。
[ERROR] An error occurred: Error occurred during query execution:
ConnectorError(ConnectorError { user_facing_error: None, kind:
QueryError(PostgresError { code: "42P05", message: "prepared statement
's0' already exists", severity: "ERROR", detail: None, column: None,
hint: None }), transient: false })
「プリペアドステートメント 's0' が既に存在する...?」
ローカルでは全く問題なかったのに、なぜ?単純な findUnique
や findMany
でさえ発生することがあり、完全に手詰まり感がありました。
AIの提案と、立ち止まる勇気
藁にもすがる思いで、Claude 3.7 に助けを求めてみました。すると、AI は「トランザクションを使えば解決するよ!コードを修正するね!」と自信満々に提案してきました。
「いや、まてまて!!」
たしかにトランザクション化するとエラーが消えるが、単純な読み取り操作でこんなエラーが出るのは明らかに異常です。Prisma の開発者がそんな場当たり的な解決策を前提とするような設計をするはずがない。
CursorのAgentは強力な助手ですが、時々誤った方向に全力で進んでいってしまいます。
コードをガシガシ書き換えるんじゃない。
これは対症療法ではなく、根本原因を突き止めるべきだと感じ、人力で調べ始めました。
嘘です。gemini 2.5 proとgensparkに泣きつきました。
gensparkのマルチモデル検索で、一気に複数のレスポンスが得られるので順番に熟読。
ここでもClaude 3.7が正解を引いてきました。
犯人はお前か!? PgBouncer という容疑者
調査を進める中で、「コネクションプーリング」というキーワードが浮上してきました。特に PostgreSQL でよく使われる PgBouncer が、セッションの扱い方によっては Prisma のような ORM と相性問題を起こすことがあるらしい、と。
Supabase も内部で PgBouncer を利用したコネクションプーリングを提供しています。もしや、これが原因では...?
見つけた!...と思ったら肩透かし
「これだ!」と思い、デプロイ用の設定ファイル .env.production
を確認すると、Supabase への接続文字列に pgbouncer=true
の文字列を発見!
「なんだ、犯人はこいつか!ローカルでは使ってなかったから気づかなかっただけだ!」
これで解決したと安堵したのも束の間、妙なことに気づきます。エラーが起きているのは、まだデプロイ前の、現在ローカルマシンからクラウドのDBに接続して動作確認をしている環境なのです。そして、その環境では .env.production
は読み込まれていませんでした。
つまり、犯人(PgBouncer設定)が見つかったはずなのに、その設定が有効でない環境でエラーが起きている...?
だめだエネルギー切れ、ちょっと休もう。
AIで調べ物もコーディングも加速したが、人間の処理能力には限りがある。
最後は人間がちゃんとチェックして、理解して、動作確認をしてからじゃなければプロダクトは完成しない。(サイバーヒャッホーな人たちは、それも要らんと言っているけどね😆)
落ち着いて見ろ、接続先のポートを
少し休憩してコーヒーを飲み、落ち着いてからもう一度、エラーが発生している環境で実際に使われている環境変数 DATABASE_URL
の中身を確認しました。
postgresql://postgres.user:<パスワード>@<supabase-project-ref>[.pooler.supabase.com:6543/postgres?schema=](https://www.google.com/search?q=https://.pooler.supabase.com:6543/postgres%3Fschema%3D)<スキーマ名>
「!!!!!」
そこに書かれていたのは、ホスト名 pooler.supabase.com
、そしてポート番号 6543
!!
Supabase のドキュメントをよく読むと、この pooler
ホスト名の 6543
ポートは、まさに PgBouncer がトランザクションプーリングモードで動作するための専用ポートだったのです。
つまり、.env.production
の設定とは関係なく、接続先のポート番号そのものが、問題の PgBouncer (トランザクションモード) を指していたわけです。
なぜポート6543だとエラーになるのか
Prisma Client は、クエリを効率化するためにプリペアドステートメントをセッション内で再利用します。しかし、PgBouncer のトランザクションプーリングモード(ポート 6543)は、トランザクションごとに接続を使い回すため、前のトランザクションで使われたプリペアドステートメントが残ったまま別の処理に使い回され、「既に存在する」というエラーを引き起こします。
一方、ローカルの Supabase コンテナや、Supabase の直接接続ポート (5432
) は、セッションプーリング(またはそれに近い動作)をするため、この問題は発生しませんでした。
解決策 (再掲)
原因がわかれば対策はシンプルです。(冒頭のまとめと同じですが、ここにも記載します)
-
直接接続を利用する:
- Supabase のダッシュボードから「Use connection pooling」のチェックを外し、表示される接続文字列(ポート
5432
、pooler
なしのホスト名)を使用する。 - 注意: 同時接続数の上限に達しやすくなる可能性があります。
- Supabase のダッシュボードから「Use connection pooling」のチェックを外し、表示される接続文字列(ポート
-
Prisma Accelerate を利用する:
- Prisma 公式のコネクションプーラーサービスを導入します。
今回は、接続数の制限を考慮しつつ、まずは直接接続(ポート 5432
)に切り替えることでエラーを解消しました。
まとめ
Supabase + Prisma の組み合わせは非常に強力ですが、Supabase が提供するコネクションプーリング(特にポート 6543
)を利用する際には注意が必要です。ローカル環境とクラウド環境での微妙な設定の違い(今回は接続先ポート)が、思わぬ落とし穴になることがあります。
この prepared statement "..." already exists
エラーに遭遇したら、まずは慌てずに接続先のホスト名とポート番号を確認し、トランザクションプーリングモードの PgBouncer に接続していないか疑ってみてください。冒頭のチェックリストが役立つはずです。
この記事が、同じ罠にはまってしまった誰かの助けになれば幸いです。
Discussion