Вопрос по асинхронному запросу к Postgresql, получение результатов

Коллеги, совершенно не понимаю в асинхронном программировании, хочу разобраться, прошу помощи. У меня написан проект с использованием библиотеки psycopg2, в качестве интерфейса для Postgresql. Я решил добавить в проект асинхронности, обновил модуль до psycopg версии 3, который поддерживает асинхронность и написал простенький пример, чтобы проиллюстрировать мои вопросы.

import asyncio
import psycopg
from datetime import datetime

async def main():

    async def open_connection():
        async with await psycopg.AsyncConnection.connect(
                dbname=dbname,
                password=password,
                host='localhost') as aconn:
            async with aconn.cursor() as cur:
                await cur.execute("select count(*) from table_1 join table_2 using (id)")
                print("\nВыполнили запрос к БД")
                return await cur.fetchall()

    async def print_monitor():
        while True:
            print(f'\r{datetime.now()}', end='')
            await asyncio.sleep(0)

    async with asyncio.TaskGroup() as tg:
        task1 = tg.create_task(open_connection())
        task2 = tg.create_task(print_monitor())

asyncio.run(main())

В примере две корутины - первая запрашивает базу, вторая постоянно печатает время (просто счетчик времени). Когда ответ на запрос возвращен базой, печатаем "Выполнили...".

Вопросы (Если требуется разбить на два отдельных вопроса - напишите в коментах)

  1. В синхронном коде метод cursor.fetchall() возвращает ответ базы (обычно это список кортежей). В асинхронном варианте он возвращает корутину. Как получить результат?
  2. Как видно из кода, вывод времени не остановится, когда запрос в базу будет выполнен. Есть ли специальные методы в модуле asyncio или еще где-то, чтобы получить подтверждение выполнения корутины и, как только она будет выполнена, прервать счетчик. Т. е., мне это видится так, что в условии while должен быть некий флаг, срабатывающий по выполнении корутины запроса к базе. Я могу, конечно, ввести nonlocal переменную в main() и использовать ее в качестве флага, но что-то мне подсказывает, что должны быть какие-то асинхронные инструменты.

UPT

Чуть изменю первый вопрос. Во всех доках и примерах написано, что и в асинхронном коде cursor.fetchall() должен вернуть результат. А у меня он возвращает корутину, значит что-то я не так написал. Что?


Ответы (1 шт):

Автор решения: CrazyElf

Используйте asyncio.Event() и task.result():

import asyncio
import psycopg
from datetime import datetime

async def main():

    async def open_connection(event):
        async with await psycopg.AsyncConnection.connect(
                dbname=dbname,
                password=password,
                host='localhost') as aconn:
            async with aconn.cursor() as cur:
                await cur.execute("select count(*) from table_1 join table_2 using (id)")
                print("\nВыполнили запрос к БД")
                event.set()
                return await cur.fetchall()

    async def print_monitor(event):
        while not event.is_set():
            print(f'\r{datetime.now()}', end='')
            await asyncio.sleep(0)

    event = asyncio.Event()
    async with asyncio.TaskGroup() as tg:
        task1 = tg.create_task(open_connection(event))
        task2 = tg.create_task(print_monitor(event))
    print(task1.result())

asyncio.run(main())
→ Ссылка