1

有没有可能@ndb.toplevel玩得很好@ndb.transactional

我想要实现的是一个包含entity.put_async()调用的事务,但方便的是不必显式等待期货。@ndb.toplevel通常会这样做,但另一个 SO 问题似乎表明它不能与事务结合使用:“ndb 顶级中断事务吗?”

我在 App Engine 文档的任何地方都找不到明确记录的内容。我们可以重现该问题中显示的断言错误,但我们编写了一些测试来查看put_async()调用是否失败并且没有发现任何问题。但是,由于可能会丢失数据,因此很高兴在这里从熟悉 ndb 的人那里获得更具体的答案。

我们的简单测试代码如下。如果我们同时删除 thendb.toplevelndb.transactional装饰器,测试将失败,正如预期的那样。但是,如果我们只使用ndb.transactional装饰器而忽略ndb.toplevel装饰器,则测试通过,这是意料之中的。这让我担心,也许ndb.transactional调用put_async()有足够的时间来完成,但没有任何保证,所以它可能会意外失败?

class AsyncTestModel(ndb.Model):
    data = ndb.StringProperty(indexed=True)

@ndb.toplevel
def start_test():
    for _ in range(100):
        test()

    # Check we wrote all the entities
    time.sleep(30)
    entities = AsyncTestModel.query().fetch()
    assert(len(entities) == 1000)


@ndb.transactional(xg=True)
def test():
    for _ in range(10):
        x = AsyncTestModel()
        x.data = make_random_string(1000)
        x.put_async()
4

1 回答 1

4

当您只使用时测试通过的事实@ndb.transactional是预期的承诺:为确保事务永远不会部分应用,@ndb.transactional请等待所有请求都完成。

所以代码for _ in range(100): test()等待事务在每次迭代时结束。

因此,以下测试通过:

@ndb.transactional(xg=True)
def test():
    for _ in range(10):
        x = AsyncTestModel()
        x.data = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(1000))
        x.put_async()

for x, _ in enumerate(range(100)):
    test()
    assert(AsyncTestModel.query().count() == (x + 1) * 10)

注意:如果您在事务中使用异步查询,您可以查看(此处@ndb.transactional_async的文档)。

于 2015-10-14T07:12:19.447 回答