Spaces:
Running
Running
| from __future__ import annotations | |
| from contextlib import asynccontextmanager | |
| from psycopg_pool import AsyncConnectionPool | |
| from app.config import get_settings | |
| _pool: AsyncConnectionPool | None = None | |
| def get_pool() -> AsyncConnectionPool: | |
| global _pool | |
| if _pool is None: | |
| settings = get_settings() | |
| _pool = AsyncConnectionPool( | |
| conninfo=settings.postgres_readonly_url, | |
| min_size=1, | |
| max_size=10, | |
| open=False, | |
| # --- ADDED TO FIX CROSS-CLOUD SSL DROPS --- | |
| max_idle=30.0, # Recycle connections that sit silent for over 30s | |
| check=AsyncConnectionPool.check_connection, # Test the connection health before handing it to your tool | |
| kwargs={ | |
| "keepalives": 1, # Turn on TCP Keepalives | |
| "keepalives_idle": 30, # Ping Railway every 30 seconds of silence | |
| "keepalives_interval": 10, # If a ping fails, retry every 10 seconds | |
| "keepalives_count": 5, # Drop the socket if 5 pings fail in a row | |
| }, | |
| ) | |
| return _pool | |
| async def open_pool() -> None: | |
| """Call on app startup.""" | |
| pool = get_pool() | |
| if pool.closed: | |
| await pool.open(wait=True) | |
| async def close_pool() -> None: | |
| """Call on app shutdown.""" | |
| global _pool | |
| if _pool is not None and not _pool.closed: | |
| await _pool.close() | |
| _pool = None | |
| async def get_readonly_connection(): | |
| """ | |
| Yields a connection with an extra read-only guard set at the | |
| session level, on top of the chat_ro_user DB role. | |
| """ | |
| pool = get_pool() | |
| async with pool.connection() as conn: | |
| await conn.execute("SET default_transaction_read_only = on") | |
| yield conn |