如何包装coroutine.__await__()?实现asyncpg连接池准确日志统计
解决asyncpg连接池获取/释放统计的时机问题
直接在__await__方法中提前更新计数器,会把「发起连接获取请求」误判为「连接获取成功」,必须等连接实际拿到手后再更新统计。以下是两种可行的解决方案:
方案1:包装__await__生成器,确保成功获取后计数
如果你的代码是通过实现__await__来包装连接获取逻辑,可以修改成如下方式:通过包装原生成器,等待异步流程完成(拿到连接)后再更新统计:
class PoolWrapper: def __init__(self, pool): self.pool = pool self.acquire_success_count = 0 self.release_count = 0 def __await__(self): # 获取原连接获取操作的生成器 acquire_gen = self.pool.acquire().__await__() try: # 完成整个异步获取流程,拿到连接对象 conn = yield from acquire_gen # 只有成功拿到连接才更新计数 self.acquire_success_count += 1 return conn except Exception: # 获取失败时不统计,直接抛出异常 raise
注意这种方式只处理了获取统计,释放操作需要手动在调用pool.release(conn)时更新计数器:
await self.pool.release(conn) self.release_count += 1
方案2:用异步上下文管理器完整统计获取/释放
更稳妥的方式是实现一个异步上下文管理器,把获取和释放的统计逻辑封装在一起,确保统计的对应性:
class PoolStatsTracker: def __init__(self, pool): self.pool = pool self.acquire_success_count = 0 self.release_count = 0 self._connection = None async def __aenter__(self): # 等待连接实际获取成功后再计数 self._connection = await self.pool.acquire() self.acquire_success_count += 1 return self._connection async def __aexit__(self, exc_type, exc_val, exc_tb): # 确保连接释放完成后再统计释放次数 await self.pool.release(self._connection) self.release_count += 1 # 可选:如果需要统计获取失败次数,可在__aenter__的异常分支中添加逻辑
使用时和原生连接池的async with语法完全一致:
async with PoolStatsTracker(your_pool) as conn: # 执行数据库操作,比如 await conn.fetch('SELECT * FROM your_table')
原实现的问题根源
原来的__await__方法在被调用时就直接更新了计数器,但此时__await__返回的只是一个异步生成器,连接获取的异步流程还没完成——只有当生成器迭代结束、拿到连接对象时,才代表连接真正获取成功。提前更新计数器会导致统计数据包含「发起但未成功的获取请求」,进而失真。
内容的提问来源于stack exchange,提问作者Ivan Bond
相关产品推荐
相关产品推荐

