You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FastAPI+SQLAlchemy+PGBouncer操作中随机出现连接关闭问题求助

问题:FastAPI+SQLAlchemy+PGBouncer 偶发连接中断错误

我用FastAPI搭配SQLAlchemy和PGBouncer,生产环境中随机出现错误:asyncpg.exceptions.ConnectionDoesNotExistError: connection was closed in the middle of operation,本地无法复现。

试过给SQLAlchemy使用NullPool连接池、将max_overflow设为0、添加pool_pre_ping配置,问题依旧存在。

我的SQLAlchemy配置

engine = create_async_engine(
    url=db_url,
    echo=False,
    pool_size=10,
    max_overflow=0,
    pool_timeout=120,
    pool_pre_ping=True,
    pool_recycle=500,
    pool_reset_on_return=None,

    connect_args={"prepared_statement_cache_size": 0, 'server_settings': {'jit': 'off'}}
)
Session = sessionmaker(
    bind=engine,
    class_=AsyncSession,
    expire_on_commit=False,
    autoflush=False,
    autocommit=False,
)

PGBouncer配置(session模式)

default_pool_size=120
max_client_con=2800
pool_mode=session
tcp_keepintvl=30 # 今日新增,正在测试效果

PG数据库的max_connections设置为2000,但应用使用连接时仍会意外断开。

SQLAlchemy会话使用方式

每个请求创建新会话,使用后关闭,上下文管理器代码如下:

@asynccontextmanager
async def get_session_context() -> AsyncSession:  # type: ignore
    async with Session() as session:
        if session is None:
            raise Exception("Database session is None")
        try:
            yield session
        except Exception as e:
            LOGGER.error(pprint.pprint(e, indent=4, depth=4))
            await session.rollback()
            raise e
        finally:
            await session.close()

我曾怀疑会话使用问题,但资料建议使用后关闭会话。想问:关闭会话会释放连接池里的连接吗?还是其他配置导致连接在使用中被断开?

更新补充日志

2025-01-25 05:24:37.372 UTC [1570] LOG:  connection authenticated: identity="postgres" method=scram-sha-256 (/var/lib/postgresql/data/pgdata/pg_hba.conf:128)

2025-01-25 05:24:37.372 UTC [1570] LOG:  connection authorized: user=postgres database=mydb

2025-01-25 05:24:37.372 UTC [58] LOG:  duration: 0.068 ms

2025-01-25 05:24:37.372 UTC [51] LOG:  duration: 0.142 ms

2025-01-25 05:24:37.372 UTC [52] LOG:  duration: 0.105 ms

2025-01-25 05:24:37.372 UTC [40] LOG:  duration: 0.122 ms

2025-01-25 05:24:37.372 UTC [107] LOG:  duration: 0.197 ms

2025-01-25 05:24:37.372 UTC [49] LOG:  duration: 0.282 ms

2025-01-25 05:24:37.372 UTC [146] LOG:  duration: 0.170 ms

2025-01-25 05:24:37.373 UTC [1327] LOG:  duration: 0.343 ms

2025-01-25 05:24:37.373 UTC [46] LOG:  duration: 0.545 ms

2025-01-25 05:24:37.373 UTC [1339] LOG:  duration: 1.119 ms

2025-01-25 05:24:37.373 UTC [169] LOG:  duration: 0.195 ms

2025-01-25 05:24:37.410 UTC [46] LOG:  duration: 36.826 ms

2025-01-25 05:24:37.424 UTC [66] LOG:  duration: 0.111 ms

2025-01-25 05:24:37.424 UTC [276] LOG:  duration: 0.055 ms

2025-01-25 05:24:37.424 UTC [275] LOG:  duration: 0.135 ms

2025-01-25 05:24:37.424 UTC [36] LOG:  duration: 0.127 ms

2025-01-25 05:24:37.424 UTC [284] LOG:  duration: 0.156 ms

2025-01-25 05:24:37.424 UTC [116] LOG:  duration: 0.133 ms
...
2025-01-25 05:37:26.250 UTC [1849] LOG:  duration: 0.093 ms

2025-01-25 05:37:26.250 UTC [37] LOG:  duration: 0.062 ms

2025-01-25 05:37:26.251 UTC [12642] LOG:  duration: 0.496 ms

2025-01-25 05:37:26.270 UTC [661] LOG:  duration: 0.190 ms

2025-01-25 05:37:26.270 UTC [178] LOG:  duration: 0.186 ms

2025-01-25 05:37:26.270 UTC [178] LOG:  duration: 0.035 ms

2025-01-25 05:37:26.274 UTC [987] LOG:  duration: 67.965 ms

2025-01-25 05:37:26.282 UTC [56] FATAL:  terminating connection due to idle-session timeout

2025-01-25 05:37:26.282 UTC [444] FATAL:  terminating connection due to idle-session timeout

2025-01-25 05:37:26.282 UTC [56] LOG:  disconnection: session time: 0:14:02.738 user=postgres database=mydb host=fd12:4d65:2636::b2:ee06:57a1 port=44082

2025-01-25 05:37:26.282 UTC [444] LOG:  disconnection: session time: 0:13:45.227 user=postgres database=mydb host=fd12:4d65:2636::b2:ee06:57a1 port=5263

日志中持续出现上述连接终止信息。


解答

关闭会话是否释放连接池连接?

是。关闭AsyncSession会将底层数据库连接归还给SQLAlchemy连接池,不会直接关闭连接,这是正确的操作,并非问题根源。

问题核心:PostgreSQL主动断开空闲会话

从日志中的FATAL: terminating connection due to idle-session timeout可以明确,PostgreSQL会自动断开空闲时间超过阈值的会话。而SQLAlchemy连接池仍持有这些已失效的连接,当后续请求复用它们时,就会触发ConnectionDoesNotExistError。

配置不匹配导致的问题

你的SQLAlchemy设置了pool_recycle=500(约8分钟),但日志中的连接空闲了14分钟才被PG断开,说明:

  • PostgreSQL的空闲会话超时阈值(idle_session_timeout或idle_in_transaction_session_timeout)大于500秒
  • SQLAlchemy的连接回收机制未及时清理即将被PG断开的连接,或pool_pre_ping在部分场景下未生效

修复方案

  1. 对齐SQLAlchemy与PG的超时配置

    • 先查询PG的空闲超时参数:
      SHOW idle_session_timeout;
      SHOW idle_in_transaction_session_timeout;
      
    • 将SQLAlchemy的pool_recycle设为比PG超时值小30-60秒,比如PG超时为840秒(14分钟),则pool_recycle设为780秒(13分钟),确保SQLAlchemy在PG断开连接前主动回收并重建连接。
  2. 优化PGBouncer配置

    • 添加server_idle_timeout,值设为比PG空闲超时小,让PGBouncer主动断开空闲的后端连接:
      server_idle_timeout=780
      
    • 可选设置client_idle_timeout,清理长时间空闲的客户端连接,避免占用PGBouncer的连接数。
  3. 确保pool_pre_ping生效
    pool_pre_ping=True会在从连接池取出连接时执行ping操作,检测连接有效性。若连接已失效,会自动重建。结合pool_recycle使用,可最大程度避免使用失效连接。

  4. 验证会话无泄漏
    给会话的创建和关闭添加日志,确认所有会话都进入finally块并被关闭,避免连接池中的连接被长期占用无法回收。


内容的提问来源于stack exchange,提问作者Confidence Yobo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 21:20:54