asyncpg连续请求触发InterfaceError: another operation is in progress报错问题
错误原因
- 核心问题是全局连接池懒加载时存在竞态条件:你使用全局变量
pool做懒加载初始化,当多个并发请求同时到达时,第一个请求还没完成asyncpg.create_pool的异步创建、还没给全局pool赋值完成,后续请求判断pool仍为None,就会同时触发多个连接池创建操作。asyncpg不允许同一上下文下并行执行未完成的数据库操作,因此抛出cannot perform operation: another operation is in progress错误。 - 额外隐患:当前的懒加载逻辑没有并发保护,即使运行过程中出现
pool意外置空的情况,也会再次触发同类竞态问题。
修复方案
方案1(更推荐):应用启动时预初始化连接池
不要用懒加载逻辑,在异步Web框架的启动钩子中提前创建好连接池,请求处理阶段直接使用已经初始化完成的池,从根源避免竞态。以FastAPI为例,修改代码如下:
import asyncpg from fastapi import FastAPI app = FastAPI() pool = None # 应用启动钩子,启动时就创建连接池 @app.on_event("startup") async def startup(): global pool pool = await asyncpg.create_pool(**config.servers.db_credentials) # 应用关闭钩子,退出时释放连接池 @app.on_event("shutdown") async def shutdown(): await pool.close() async def query(sql): global pool print(sql) async with pool.acquire() as connection: async with connection.transaction(): result = await connection.fetch(sql) print(result) return result # 接口逻辑不变 @app.route("/getreportdetails/") async def data(): sql = "select * from get_report_details" report_data = await query(sql) # 补充后续返回逻辑即可
方案2:给懒加载逻辑加异步锁
如果你必须保留懒加载逻辑,用asyncio.Lock保证连接池只被初始化一次:
import asyncio import asyncpg pool = None # 初始化锁,保证同一时间只有一个协程能执行初始化逻辑 init_lock = asyncio.Lock() async def get_pool(): return await asyncpg.create_pool(**config.servers.db_credentials) async def query(sql): global pool print(sql) # 双重检查+锁,避免每次请求都加锁影响性能 if pool is None: async with init_lock: # 拿到锁之后再判断一次,避免前面的协程已经初始化完成 if pool is None: pool = await get_pool() async with pool.acquire() as connection: async with connection.transaction(): result = await connection.fetch(sql) print(result) return result
内容的提问来源于stack exchange,提问作者Poorva
相关产品推荐
相关产品推荐

