mirror of
https://github.com/hanzoai/bootnode.git
synced 2026-09-30 20:46:32 +00:00
fix: convert DATABASE_URL to asyncpg scheme for async SQLAlchemy engine
The K8s DATABASE_URL env var uses postgresql:// which makes create_async_engine try to load psycopg2 (sync driver). Add _ensure_async_dsn() to convert postgresql:// and postgres:// to postgresql+asyncpg:// at engine creation time. asyncpg is already in dependencies.
This commit is contained in:
@@ -62,6 +62,7 @@ test-*.mjs
|
||||
test-*.js
|
||||
test_*.mjs
|
||||
test_*.py
|
||||
!api/tests/test_*.py
|
||||
*-test-results.json
|
||||
*-results.json
|
||||
*-ui-inspection.json
|
||||
|
||||
@@ -8,8 +8,25 @@ from bootnode.config import get_settings
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
|
||||
def _ensure_async_dsn(dsn: str) -> str:
|
||||
"""Ensure the database DSN uses the asyncpg driver.
|
||||
|
||||
Converts ``postgresql://`` or ``postgres://`` to
|
||||
``postgresql+asyncpg://`` so that SQLAlchemy's async engine
|
||||
uses the correct driver regardless of how DATABASE_URL is set.
|
||||
"""
|
||||
if dsn.startswith("postgresql+asyncpg://"):
|
||||
return dsn
|
||||
if dsn.startswith("postgresql://"):
|
||||
return dsn.replace("postgresql://", "postgresql+asyncpg://", 1)
|
||||
if dsn.startswith("postgres://"):
|
||||
return dsn.replace("postgres://", "postgresql+asyncpg://", 1)
|
||||
return dsn
|
||||
|
||||
|
||||
engine = create_async_engine(
|
||||
str(settings.database_url),
|
||||
_ensure_async_dsn(str(settings.database_url)),
|
||||
pool_size=settings.db_pool_size,
|
||||
max_overflow=settings.db_max_overflow,
|
||||
echo=settings.debug,
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
"""Tests for database session DSN handling."""
|
||||
|
||||
from bootnode.db.session import _ensure_async_dsn
|
||||
|
||||
|
||||
def test_postgresql_converted():
|
||||
"""Standard postgresql:// is converted to asyncpg."""
|
||||
dsn = "postgresql://user:pass@host:5432/db"
|
||||
assert _ensure_async_dsn(dsn) == "postgresql+asyncpg://user:pass@host:5432/db"
|
||||
|
||||
|
||||
def test_postgres_converted():
|
||||
"""Short-form postgres:// is converted to asyncpg."""
|
||||
dsn = "postgres://user:pass@host:5432/db"
|
||||
assert _ensure_async_dsn(dsn) == "postgresql+asyncpg://user:pass@host:5432/db"
|
||||
|
||||
|
||||
def test_asyncpg_unchanged():
|
||||
"""Already correct DSN is returned as-is."""
|
||||
dsn = "postgresql+asyncpg://user:pass@host:5432/db"
|
||||
assert _ensure_async_dsn(dsn) == dsn
|
||||
|
||||
|
||||
def test_production_dsn():
|
||||
"""Matches the actual K8s DATABASE_URL format."""
|
||||
dsn = "postgresql://bootnode:[email protected]:5432/bootnode"
|
||||
expected = "postgresql+asyncpg://bootnode:[email protected]:5432/bootnode"
|
||||
assert _ensure_async_dsn(dsn) == expected
|
||||
|
||||
|
||||
def test_only_first_occurrence_replaced():
|
||||
"""Only the scheme prefix is replaced, not embedded substrings."""
|
||||
dsn = "postgresql://user:postgresql@host:5432/postgresql"
|
||||
result = _ensure_async_dsn(dsn)
|
||||
assert result == "postgresql+asyncpg://user:postgresql@host:5432/postgresql"
|
||||
Reference in New Issue
Block a user