Ndb.transaction_async() против асинхронных вызовов внутри транзакции

Я в процессе перемещения моего ndb codebase в async, насколько это возможно, где это имеет смысл. Существует сценарий, когда я не совсем уверен, как действовать: транзакции.

Как я вижу, у меня есть 3 варианта:

Вариант 1. Вызовите ndb.transaction() синхронно и вызовите методы асинхронной работы функции транзакции.

def option_1():
    @ndb.tasklet
    def txn():
        e = yield Foo.get_by_id_async(some_id)
        if e is None:
            e = yield Foo().put_async()
        raise ndb.Return(e)

    # The doc for ndb.transaction() says that it takes a function or tasklet.
    # If it a tasklet, does it wait on the Future that is returned?
    # If it doesn't wait, what is the proper way to call a tasklet in a transaction
    # and get access to its return value?
    return ndb.transaction(txn)

Вариант 2. Вызовите ndb.transaction() асинхронно и используйте методы синхронизации вызова функции транзакции.

@ndb.toplevel
def option_2():
    def txn():
        e = Foo.get_by_id(some_id)
        if e is None:
            e = Foo().put()
        return e

    result = yield ndb.transaction_async(txn)
    return result

Вариант 3: асинхронно вызовите ndb.transaction() и вызовите методы асинхронной транзакции.

@ndb.toplevel
def option_3():
    @ndb.tasklet
    def txn():
        e = yield Foo.get_by_id_async(some_id)
        if e is None:
            e = yield Foo().put_async()
        raise ndb.Return(e)

    result = yield ndb.transaction_async(txn)
    return result

Мне кажется, что вариант 3 - это тот, на который нужно пойти, но я бы предпочел полагаться на экспертное мнение/совет...

Ответ 1

Да, №3 - это определенно путь. У двух других есть проблема, что они смешивают асинхронный и синхронизирующий код, и обычно это не очень хорошо, если только на самом верхнем уровне вашего приложения.

PS. В действительности транзакция ожидает обратного вызова, если она является тасклетом.