从数据存储区读取 (cross-group) 个实体时出现 TransactionFailedError(争用太多...)
TransactionFailedError (too much contention...) when reading (cross-group) entities from datastore
我正在再次调查意外发生的 TransactionFailedError (too much contention on these datastore entities...
情况,其中代码仅读取因争用问题而受到指责的实体组。
设置
GAE 标准环境,Python 2.7 with NDB (SDK 1.9.51)。我设法在一个孤立的应用程序(只有我作为用户)中观察到错误,其中在任务队列中执行相同的请求处理程序并且 read/write 访问下面提到的 entity-groups 仅由该处理程序完成。
处理程序每秒执行几次,基本上是一个迁移/复制任务,将现有的 OriginChild
实体从一个巨大的组中移动到单独的组中作为新的 Target
实体。每个 OriginChild
个实体一个任务。
在跨组事务函数中,ndb.transaction(lambda: main_activity(), xg=True)
,每个请求处理程序...:
确实使用 get_async NDB tasklet 检索两个实体:
Key(OriginGroup, 1)
(所有请求都一样)
Key(OriginGroup, 1, OriginChild, Foo)
(每个请求唯一 object)
是否Key(TargetConfig, 1).get()
有没有Key(Target, Foo).get()
(真的没有parent!)
如果 Key 不存在,在交易函数离开之前 Key(Target, Foo).put_async()
和 get_result()
因此,这些是交易中的 read-only 个实体:
Key(Origin, 1)
Key(Origin, 1, OriginChild, Foo)
Key(TargetConfig, 1)
代码没有做任何更改,这些实体没有被删除或写回数据存储区。此外,没有其他请求 运行ning 尝试写入这些实体组 - 几个月来这些组中根本没有写入操作)。
放入数据存储区的唯一实体是 Key(Target, Foo)
,其中每个请求的 ID 都是唯一的。
错误
大约 60-70% 的请求会 运行 with-out 错误。
当 TransactionFailedError 发生时,它将在事务函数内部,日志显示如下:
suspended generator get(context.py:758) raised TransactionFailedError(too much contention on these datastore entities. please try again. entity group key: app: "e~my-test-app"
name_space: "test"
path <
Element {
type: "OriginGroup"
id: 1
}
>
)
在大约 80% 的失败请求中,错误将与 Key(OriginGroup, 1)
相关(尽管使用了整个组 read-only)。
在大约 10% 的失败请求中,错误将显示 Key(TargetConfig, 1)
(read-only)。
在剩下的 ~10% 中,它将归咎于新实体,例如Key(Target, Foo)
,或者对于请求执行迁移的任何 TargetChild 的 ID,它似乎只在 put()
期间发生,而不是之前的 get()
尝试。
理论
我对事务和实体组的理解是 NDB 遵循 乐观并发控制 ,因此来自同一个 entity-group 的大量读取操作是可能的(因此可扩展性),并且由于仅针对事务性写入操作的技术原因,存在每秒每个实体组约 1 个写入操作的限制,并且每个事务不超过 25 个实体组。
但是,我的观察表明读取操作也会导致 too much contention
错误。但是这个想法也让我感到困惑,因为如果您的目标是强一致性,它会使带有 Datastore 的 GAE 的可扩展性大大降低。所以也许这里发生了其他事情。
我发现关于 SO 的评论声称我的假设是正确的:
"Note: The first read of an entity group in an XG transaction may throw a TransactionFailedError exception if there is a conflict with other transactions accessing that same entity group. This means that even an XG transaction that performs only reads can fail with a concurrency exception."
来源:
我能够在新文档中找到引用,现在在 Superseded Storage Solutions > DB Client Library for Cloud Datastore > Overview
下
问题
引用的语句是否仍然适用于 NDB(或仅适用于版本冲突的 DB and/or)?
如果为真:建议采用哪种模式来避免跨实体组事务读取的争用错误?
在至少有一次写入的事务中,在本例中 Key(Target, Foo)
,Cloud Datastore 会将无操作标记写入已读取但未修改的实体组。这是为了确保可序列化。
因为 Key(OriginGroup, 1)
并且您在很长一段时间内以每秒超过 1 笔的速度执行 XG 交易,这就是我们争论的根源。
要考虑的一种替代方法是一次写入 23 Key(Target, Foo)
个实体而不是一个的批处理策略。 Key(Origin, 1)
和 Key(TargetConfig, 1)
占用另外 2 个实体组槽。
我正在再次调查意外发生的 TransactionFailedError (too much contention on these datastore entities...
情况,其中代码仅读取因争用问题而受到指责的实体组。
设置
GAE 标准环境,Python 2.7 with NDB (SDK 1.9.51)。我设法在一个孤立的应用程序(只有我作为用户)中观察到错误,其中在任务队列中执行相同的请求处理程序并且 read/write 访问下面提到的 entity-groups 仅由该处理程序完成。
处理程序每秒执行几次,基本上是一个迁移/复制任务,将现有的 OriginChild
实体从一个巨大的组中移动到单独的组中作为新的 Target
实体。每个 OriginChild
个实体一个任务。
在跨组事务函数中,ndb.transaction(lambda: main_activity(), xg=True)
,每个请求处理程序...:
确实使用 get_async NDB tasklet 检索两个实体:
Key(OriginGroup, 1)
(所有请求都一样)Key(OriginGroup, 1, OriginChild, Foo)
(每个请求唯一 object)
是否
Key(TargetConfig, 1).get()
有没有
Key(Target, Foo).get()
(真的没有parent!)如果 Key 不存在,在交易函数离开之前
Key(Target, Foo).put_async()
和get_result()
因此,这些是交易中的 read-only 个实体:
Key(Origin, 1)
Key(Origin, 1, OriginChild, Foo)
Key(TargetConfig, 1)
代码没有做任何更改,这些实体没有被删除或写回数据存储区。此外,没有其他请求 运行ning 尝试写入这些实体组 - 几个月来这些组中根本没有写入操作)。
放入数据存储区的唯一实体是 Key(Target, Foo)
,其中每个请求的 ID 都是唯一的。
错误
大约 60-70% 的请求会 运行 with-out 错误。
当 TransactionFailedError 发生时,它将在事务函数内部,日志显示如下:
suspended generator get(context.py:758) raised TransactionFailedError(too much contention on these datastore entities. please try again. entity group key: app: "e~my-test-app"
name_space: "test"
path <
Element {
type: "OriginGroup"
id: 1
}
>
)
在大约 80% 的失败请求中,错误将与 Key(OriginGroup, 1)
相关(尽管使用了整个组 read-only)。
在大约 10% 的失败请求中,错误将显示 Key(TargetConfig, 1)
(read-only)。
在剩下的 ~10% 中,它将归咎于新实体,例如Key(Target, Foo)
,或者对于请求执行迁移的任何 TargetChild 的 ID,它似乎只在 put()
期间发生,而不是之前的 get()
尝试。
理论
我对事务和实体组的理解是 NDB 遵循 乐观并发控制 ,因此来自同一个 entity-group 的大量读取操作是可能的(因此可扩展性),并且由于仅针对事务性写入操作的技术原因,存在每秒每个实体组约 1 个写入操作的限制,并且每个事务不超过 25 个实体组。
但是,我的观察表明读取操作也会导致 too much contention
错误。但是这个想法也让我感到困惑,因为如果您的目标是强一致性,它会使带有 Datastore 的 GAE 的可扩展性大大降低。所以也许这里发生了其他事情。
我发现关于 SO 的评论声称我的假设是正确的:
"Note: The first read of an entity group in an XG transaction may throw a TransactionFailedError exception if there is a conflict with other transactions accessing that same entity group. This means that even an XG transaction that performs only reads can fail with a concurrency exception."
来源:
我能够在新文档中找到引用,现在在 Superseded Storage Solutions > DB Client Library for Cloud Datastore > Overview
下问题
引用的语句是否仍然适用于 NDB(或仅适用于版本冲突的 DB and/or)?
如果为真:建议采用哪种模式来避免跨实体组事务读取的争用错误?
在至少有一次写入的事务中,在本例中 Key(Target, Foo)
,Cloud Datastore 会将无操作标记写入已读取但未修改的实体组。这是为了确保可序列化。
因为 Key(OriginGroup, 1)
并且您在很长一段时间内以每秒超过 1 笔的速度执行 XG 交易,这就是我们争论的根源。
要考虑的一种替代方法是一次写入 23 Key(Target, Foo)
个实体而不是一个的批处理策略。 Key(Origin, 1)
和 Key(TargetConfig, 1)
占用另外 2 个实体组槽。