Не удаётся подключится к боту и базе данных асинхронно
Пытаюсь соединить 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())