围绕Google Cloud Datastore嵌套事务的意外行为

问题描述 投票:0回答:1

我遇到的业务问题是:父实体具有子实体后代。子实体具有需要唯一的值,因此存在一个ChildLookup实体以强制执行该唯一性。为了抽象一些东西,实体的放置/删除操作已放入其自己的方法中,并且它们的逻辑中都包含批处理/事务处理语句。

在Python中(使用this library),当结构是这样时,一切都很好:

# assuming ('Parent', 11), ('Parent', 11, 'Child', 'foo') (with value 'bar'), and ('ChildLookup-foo', 'bar') all exist

def put_parent(c):
 p2 = c.get(c.key('Parent', 22))
 if p2 is None:
  p2 = Entity(c.key('Parent', 22))
 c.put(p2)

def put_child(c):
 with c.transaction():
  e2 = Entity(c.key(*p2.key.flat_path, 'Child', 'foo'))
  e2['__val'] = 'bar'
  el2 = c.get(c.key('ChildLookup-foo', e2['__val']))
  if el2 is not None:
   raise ValueError('insert would create duplicate')
  el2 = Entity(c.key('ChildLookup-foo', 'val'))
  c.put(el2)
  c.put(e2)

c = google.cloud.datastore.Client()

with c.transaction():
 put_parent(c)
 put_child(c)

尝试运行此操作将导致正确的行为:将引发异常,并且不会插入p2或e2。但是,我可以将put_parent更改为如下形式:

def put_parent():
 with c.transaction():  # only actual change. can also be c.batch()
  p2 = c.get(c.key('Parent', 22))
  if p2 is None:
   p2 = Entity(c.key('Parent', 22))
  c.put(p2)

当我这样做时,尽管第二笔交易回滚,但仍插入了p2。这对我来说是意外的:我希望回滚只限于最内部的事务(或批处理),或者我希望回滚会影响最外部的事务(或批处理)的所有子事务。

当然,在上面的琐碎玩具示例中,我可以取出内部批处理并从顶层进行管理。但是,将它们放入方法中的要点是,我有时可能想单独调用它们,而不会从调用它们的方法中获得相同的保证,并且我希望它们的事务性要求对这些方法的使用者而言并不重要。是否有一种设计模式或某些Python Google Cloud Datastore库功能可以让我做我想做的事情?

python google-cloud-datastore
1个回答
0
投票

您是否尝试过c.current_transaction()?

https://googleapis.dev/python/datastore/latest/client.html

想法是,您使用

使用c.transaction()

在您的所有通话之外以及每个通话中,只需获取当前交易并使用该交易即可完成操作。我认为您不应该在函数中使用“ with”,因为这样会在最后自动提交/回滚。

因此,将类似于以下内容。

def put_parent(c):
 txn = c.current_transaction()
 p2 = txn.get(c.key('Parent', 22))
 if p2 is None:
  p2 = Entity(c.key('Parent', 22))
 txn.put(p2)

def put_child(c):
  txn = c.current_transaction()
  e2 = Entity(c.key(*p2.key.flat_path, 'Child', 'foo'))
  e2['__val'] = 'bar'
  el2 = txn.get(c.key('ChildLookup-foo', e2['__val']))
  if el2 is not None:
   raise ValueError('insert would create duplicate')
  el2 = Entity(c.key('ChildLookup-foo', 'val'))
  txn.put(el2)
  txn.put(e2)

c = google.cloud.datastore.Client()

with c.transaction():
 put_parent(c)
 put_child(c)
© www.soinside.com 2019 - 2024. All rights reserved.