如何在aiogram中正确使用asyncpg?连接池报错排查
aiogram + asyncpg 事件循环不匹配导致的InterfaceError问题解决
问题描述
基于aiogram框架开发Telegram Bot时,使用asyncpg库操作PostgreSQL数据库。数据库类初始化阶段通过asyncio.get_event_loop()同步执行create_pool创建连接池,程序启动时显示数据库连接成功,但首次发起数据库请求时抛出InterfaceError: cannot perform operation: another operation is in progress,同时伴随RuntimeError: Task got Future attached to a different loop的事件循环不匹配错误。
相关代码
数据库类代码
class DataBase: def __init__(self): self.db_pool = None loop = asyncio.get_event_loop() loop.run_until_complete(self.create_pool()) async def create_pool(self): try: self.db_pool = await asyncpg.create_pool(Config.POSTGRES_URI) async with self.db_pool.acquire() as connection: await connection.execute('SELECT 1') print("\tSuccessful connection to the database") except Exception as e: print(f"\tDatabase connection error: {e}")
报错信息
Cause exception while process update id=56403182 by bot id=6663434503 InterfaceError: cannot perform operation: another operation is in progress Traceback (most recent call last): File "beauty_experts_bot/src/services/sql.py", line 27, in add_user await connection.execute("""INSERT INTO user (id, username, name) VALUES (?, ?, ?)""", File "beauty_experts_bot/venv/lib64/python3.11/site-packages/asyncpg/connection.py", line 319, in execute _, status, _ = await self._execute( ^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib64/python3.11/site-packages/asyncpg/connection.py", line 1659, in _execute result, _ = await self.__execute( ^^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib64/python3.11/site-packages/asyncpg/connection.py", line 1684, in __execute return await self._do_execute( ^^^^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib64/python3.11/site-packages/asyncpg/connection.py", line 1711, in _do_execute stmt = await self._get_statement( ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib64/python3.11/site-packages/asyncpg/connection.py", line 397, in _get_statement statement = await self._protocol.prepare( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "asyncpg/protocol/protocol.pyx", line 168, in prepare RuntimeError: Task <Task pending name='Task-29' coro=<Dispatcher._process_update() running at /home/davyd/MY_DATA/programming/tmp_project/beauty_experts_bot/venv/lib/python3.11/site-packages/aiogram/dispatcher/dispatcher.py:308>> got Future <Future pending cb=[Protocol._on_waiter_completed()]> attached to a different loop During handling of the above exception, another exception occurred: Traceback (most recent call last): File "beauty_experts_bot/venv/lib/python3.11/site-packages/aiogram/dispatcher/dispatcher.py", line 308, in _process_update response = await self.feed_update(bot, update, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib/python3.11/site-packages/aiogram/dispatcher/dispatcher.py", line 157, in feed_update response = await self.update.wrap_outer_middleware( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib/python3.11/site-packages/aiogram/dispatcher/middlewares/error.py", line 25, in __call__ return await handler(event, data) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib/python3.11/site-packages/aiogram/dispatcher/middlewares/user_context.py", line 27, in __call__ return await handler(event, data) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib/python3.11/site-packages/aiogram/fsm/middleware.py", line 39, in __call__ return await handler(event, data) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib/python3.11/site-packages/aiogram/dispatcher/event/telegram.py", line 120, in trigger return await wrapped_inner(event, kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib/python3.11/site-packages/aiogram/dispatcher/event/handler.py", line 42, in call return await wrapped() ^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib/python3.11/site-packages/aiogram/dispatcher/dispatcher.py", line 275, in _listen_update return await self.propagate_event(update_type=update_type, event=event, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib/python3.11/site-packages/aiogram/dispatcher/router.py", line 116, in propagate_event return await observer.wrap_outer_middleware(_wrapped, event=event, data=kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib/python3.11/site-packages/aiogram/dispatcher/router.py", line 111, in _wrapped return await self._propagate_event( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib/python3.11/site-packages/aiogram/dispatcher/router.py", line 136, in _propagate_event response = await observer.trigger(event, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib/python3.11/site-packages/aiogram/dispatcher/event/telegram.py", line 120, in trigger return await wrapped_inner(event, kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "beauty_experts_bot/venv/lib/python3.11/site-packages/aiogram/dispatcher/event/handler.py", line 42, in call return await wrapped() ^^^^^^^^^^^^^^^ File "beauty_experts_bot/src/handlers/basic.py", line 15, in start await db.add_user( File "beauty_experts_bot/src/services/sql.py", line 26, in add_user async with self.db_pool.acquire() as connection: File "beauty_experts_bot/venv/lib64/python3.11/site-packages/asyncpg/pool.py", line 220, in release raise ex File "beauty_experts_bot/venv/lib64/python3.11/site-packages/asyncpg/pool.py", line 210, in release await self._con.reset(timeout=budget) File "beauty_experts_bot/venv/lib64/python3.11/site-packages/asyncpg/connection.py", line 1366, in reset await self.execute(reset_query, timeout=timeout) File "beauty_experts_bot/venv/lib64/python3.11/site-packages/asyncpg/connection.py", line 317, in execute return await self._protocol.query(query, timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "asyncpg/protocol/protocol.pyx", line 323, in query File "asyncpg/protocol/protocol.pyx", line 707, in asyncpg.protocol.protocol.BaseProtocol._check_state asyncpg.exceptions._base.InterfaceError: cannot perform operation: another operation is in progress
数据库请求代码
db = DataBase() async def start(message: Message) -> None: await db.add_user( user_id=message.from_user.id, username=message.from_user.username, name=message.from_user.full_name )
Bot启动代码
from aiogram import Bot, Dispatcher import asyncio from aiogram.enums.parse_mode import ParseMode from config import Config from handlers import basic, specialist, admin from utils.commands import set_commands async def start(): bot = Bot(token=Config.TOKEN, parse_mode=ParseMode.HTML) dp = Dispatcher(bot=bot) # REGISTER HANDLERS basic.register_handlers(dp=dp) specialist.register_handlers(dp=dp) admin.register_handlers(dp=dp) await set_commands(bot=bot) try: await dp.start_polling(bot) finally: await bot.send_message(chat_id=Config.ADMIN_ID, text="<b>BOT STOPPED</b>") await bot.session.close() if __name__ == "__main__": asyncio.run(start())
问题根源
核心问题是事件循环不匹配:
- 当
db = DataBase()执行时,__init__方法通过asyncio.get_event_loop()获取了当前主线程的事件循环,并在该循环中创建了数据库连接池。 - 而
asyncio.run(start())会创建一个全新的事件循环来运行Bot的异步逻辑,这个新循环和连接池所属的旧循环不是同一个对象。 - 当Bot的处理任务(运行在新循环)尝试使用连接池时,就会出现"Future attached to a different loop"的错误,进而引发InterfaceError。
解决方案
1. 修改数据库类,改为异步初始化
去掉__init__中的同步创建逻辑,添加异步初始化方法:
class DataBase: def __init__(self): self.db_pool = None async def setup(self): try: self.db_pool = await asyncpg.create_pool(Config.POSTGRES_URI) async with self.db_pool.acquire() as connection: await connection.execute('SELECT 1') print("\tSuccessful connection to the database") except Exception as e: print(f"\tDatabase connection error: {e}")
2. 在Bot启动逻辑中异步初始化数据库
调整start函数,先初始化数据库再启动Bot:
# 全局只声明db对象,不立即初始化 db = DataBase() async def start(): bot = Bot(token=Config.TOKEN, parse_mode=ParseMode.HTML) dp = Dispatcher(bot=bot) # 先初始化数据库 await db.setup() # REGISTER HANDLERS basic.register_handlers(dp=dp) specialist.register_handlers(dp=dp) admin.register_handlers(dp=dp) await set_commands(bot=bot) try: await dp.start_polling(bot) finally: await bot.send_message(chat_id=Config.ADMIN_ID, text="<b>BOT STOPPED</b>") await bot.session.close() # 关闭数据库连接池 if db.db_pool: await db.db_pool.close() if __name__ == "__main__": asyncio.run(start())
3. 修正SQL占位符(额外问题)
asyncpg使用$1, $2, $3作为占位符,而不是?,同时user是PostgreSQL保留字,需要用双引号包裹表名,修改add_user方法中的SQL语句:
async def add_user(self, user_id, username, name): async with self.db_pool.acquire() as connection: await connection.execute(""" INSERT INTO "user" (id, username, name) VALUES ($1, $2, $3) """, user_id, username, name)
验证
修改完成后重新启动Bot,首次发起数据库请求时将不再出现事件循环不匹配的错误,数据库操作可以正常执行。
内容的提问来源于stack exchange,提问作者Maxwell
相关产品推荐
相关产品推荐

