Лучший способ открыть / закрыть соединение с БД с помощью async/await

В обучающих программах, которые я обнаружил, всегда открываются и закрываются соединения для каждого запроса, например:

import asyncio
import asyncpg

async def run():
    conn = await asyncpg.connect(user='user', password='password',
                             database='database', host='127.0.0.1')
    values = await conn.fetch('''SELECT * FROM mytable''')
    await conn.close()

loop = asyncio.get_event_loop()
loop.run_until_complete(run())

Хотя это работает для одной функции, а как насчет веб-приложения?

IE: например, в Tornado каждый URL является классом, что приводит к множеству классов / методов.

У меня есть привычка открывать соединение блокирующим образом, а затем использовать оболочку для выполнения асинхронных вызовов БД и закрывать соединение только для того, чтобы грациозно завершить работу сервера, что является наилучшей практикой в ​​этом случае async/await?

1 ответ

Решение

Не используя asyncpg, я предполагаю, что, как и в большинстве пакетов, совместимых с asyncio, существует менеджер контекста async, позволяющий выполнять именно то, что вы запрашиваете.

Что-то вроде:

async with asyncpg.create_pool(**kwargs) as pool:
    async with pool.acquire() as connection:
        async with connection.transaction():
            result = await connection.fetchval(fetch stuff)
            connection.execute(insert stuff with result)

(как взято из этого вопроса)

Проверьте документы для упоминания контекстных менеджеров или примеров с async with заявления или, если ничего другого, то проверьте классы в исходном коде, которые реализуют __aenter__, __aexit__ методы.

Изменить 1:

Приведенный выше пример частично взят из вопроса, с которым я связан, и частично придуман для полноты. Но чтобы ответить на ваши комментарии о том, что делают операторы with:

async with asyncpg.create_pool(**kwargs) as pool:
    #in this block pool is created and open
    async with pool.acquire() as connection:
        # in this block connection is acquired and open
        async with connection.transaction():
            # in this block each executed statement is in a transaction
            execute_stuff_with_connection(connection)
        # now we are back up one logical block so the transaction is closed
        do_stuff_without_transaction_but_with_connection(connection)
    # now we are up another block and the connection is closed and returned to the pool
    do_more_stuff_with_pool(pool)
# now we are up another level and the pool is closed/exited/cleaned up
done_doing_async_stuff()

Я не уверен, насколько это хорошее объяснение, возможно, вам стоит ознакомиться с менеджерами контекста.

Другие вопросы по тегам