erdoganpeker commited on
Commit
d0ce1e8
·
1 Parent(s): f4bb922

fix(deploy): auto-disable prepared statements on Supabase pooler

Browse files

asyncpg's DuplicatePreparedStatementError fires when running against
PgBouncer in transaction-pool mode (Supabase shared pooler). The
DB_USE_PGBOUNCER env was already wired but easy to forget on deploy.

Auto-detect Supabase pooler from the URL (port 6543 or
pooler.supabase.com host) and force statement_cache_size=0 +
prepared_statement_cache_size=0. Explicit DB_USE_PGBOUNCER=true still
honoured for other PgBouncer deployments.

Fixes: prepared statement "__asyncpg_stmt_1__" already exists during
alembic upgrade head on Render.

Files changed (1) hide show
  1. services/backend/database.py +8 -1
services/backend/database.py CHANGED
@@ -111,7 +111,14 @@ _MAX_OVERFLOW = int(os.getenv("DB_MAX_OVERFLOW", "20"))
111
  _POOL_TIMEOUT = int(os.getenv("DB_POOL_TIMEOUT", "30"))
112
  _POOL_RECYCLE = int(os.getenv("DB_POOL_RECYCLE", "1800"))
113
  _SQL_ECHO = os.getenv("DB_ECHO", "false").lower() == "true"
114
- _USE_PGBOUNCER = os.getenv("DB_USE_PGBOUNCER", "false").lower() == "true"
 
 
 
 
 
 
 
115
 
116
 
117
  def _create_engine(url: str = DATABASE_URL) -> AsyncEngine:
 
111
  _POOL_TIMEOUT = int(os.getenv("DB_POOL_TIMEOUT", "30"))
112
  _POOL_RECYCLE = int(os.getenv("DB_POOL_RECYCLE", "1800"))
113
  _SQL_ECHO = os.getenv("DB_ECHO", "false").lower() == "true"
114
+ # Auto-detect: any Supabase pooler URL (port 6543 or "pooler.supabase.com" in host)
115
+ # implies pgBouncer transaction mode → prepared statements MUST be disabled.
116
+ # Explicit DB_USE_PGBOUNCER=true still wins for non-Supabase pgBouncer setups.
117
+ _USE_PGBOUNCER = (
118
+ os.getenv("DB_USE_PGBOUNCER", "false").lower() == "true"
119
+ or ":6543/" in DATABASE_URL
120
+ or "pooler.supabase.com" in DATABASE_URL
121
+ )
122
 
123
 
124
  def _create_engine(url: str = DATABASE_URL) -> AsyncEngine: