Не удаётся подключится к боту и базе данных асинхронно
Пытаюсь соединить Aiogram3 и asyncpg, но столкнулся с недостатком понимания асинхронности.
Я пытался сделать запуск БД и бота таким образом:
dp = Dispatcher()
adb = DB(DSN)
@dp.message(CommandStart)
async def start(message: Message):
async with adb.pool.acquire() as conn: # взять подключение из пула и потом вернуть обратно;
x = await conn.fetch('SELECT * FROM manga;')
print(x)
await message.answer('Работает.')
async def main():
bot = Bot(token=TOKEN)
await adb.connect()
await dp.start_polling(bot)
if __name__ == '__main__':
asyncio.run(main())
class DB:
def __init__(self, dsn):
self.dsn = dsn
self.pool = None
async def connect(self):
self.pool = await asyncpg.create_pool(self.dsn) # создать пулл подключений;
async def close(self):
await self.pool.close()
Но я понял, что асинхронностю тут и не пахнет, две корутины блокируют друг друга и работают последовательно, а не асинхронно.
Я в принципе не нашёл достойных примеров применения asyncpg, хотя вроде как эта библиотека вне конкуренции по скорости среди всех подобных, так как использует не текстовые инструкции, а обращение к байт коду через C.
Если есть какие-нибудь best practices, я буду очень рад почитать.
Ответы (2 шт):
Проблема решена созданием задач:
async def main():
bot = Bot(token=TOKEN)
task1 = asyncio.create_task(adb.connect())
task2 = asyncio.create_task(dp.start_polling(bot))
await task1
await task2
if __name__ == '__main__':
asyncio.run(main())
Также, используя gather, удалось сделать то же самое, но короче и проще.
async def main():
bot = Bot(token=TOKEN)
task1 = asyncio.create_task(adb.connect())
task2 = asyncio.create_task(dp.start_polling(bot))
await asyncio.gather(task1, task2)
if __name__ == '__main__':
asyncio.run(main())
Но даже с таким подходом иногда возникали ошибки, что нет такого метода .acquire(), потому что БД не успевало создать пулл подключений, когда бот уже запускался. Или вроде того.
Возможно такой способ поможет решить проблему. За основу я взял ваш код, поэтому класс будет таким же.
class DB:
def __init__(self, dsn):
self.dsn = dsn
self.pool = None
async def get_connection(self):
return self.pool
async def connect(self):
self.pool = await asyncpg.create_pool(self.dsn)
async def close(self):
await self.pool.close()
Указываем параметры подключения, в нашем случае dns postgres://user:password@host:port/database.
db = DB("postgres://user:password@host:port/database")
Из переменной db обращаемся к методу connect(), для того чтобы создать пул, а затем обращаемся к get_connection(), чтобы получить пул. А затем можем работать с базой данных.
async def main():
await db.connect()
pool = await db.get_connection()
async with pool.acquire() as conn:
print(await conn.fetchrow("""SELECT * FROM users"""))
asyncio.run(main())