使用游标的Google应用引擎数据存储区查询不会迭代所有项目

时间:2015-05-28 10:03:58

标签: google-app-engine go google-cloud-datastore

在我的应用程序中,我有一个带过滤器的数据存储区查询,例如:

22.0.1

我正在使用游标迭代批量结果(例如,在不同的任务中)。如果在迭代它时更改datastore.NewQuery("sometype").Filter("SomeField<", 10) 的值,则光标将不再适用于谷歌应用引擎(在devappserver上正常工作)。

我在这里有一个测试项目:https://github.com/fredr/appenginetest 在我的测试中,我运行了SomeField,它将为数据库设置10个项目,其值设置为0,然后运行/db,它将遍历值小于2的所有项目,批量为5 ,并将每个项目的值更新为2。

我本地devappserver上的结果(所有项目都已更新): devappserver result

appengine的结果(只更新了五个项目): appengine result

我做错了吗?这是一个错误吗?或者这是预期的结果? 在文档中说明:

  

对于在具有多个值的属性上使用不等式过滤器或排序顺序的查询,游标并不总是按预期工作。

1 个答案:

答案 0 :(得分:3)

问题是游标的性质和实现。游标包含最后处理的实体的密钥(已编码),因此如果在执行之前将游标设置为查询,则数据存储将跳转到由游标中编码的密钥指定的实体,并将从那一点。

让我们检查你的案例

您的查询过滤器为Value<2。您迭代查询结果的实体,并将Value属性更改(并保存)为2请注意,Value=2不符合过滤器Value<2

在下一次迭代(下一批)中,存在一个正确应用的光标。因此,当数据存储区执行查询时,它会跳转到上一次迭代中处理的最后一个实体,并希望列出此后的实体。但是光标指向的实体可能已经不满足过滤器;因为新值2的索引条目很可能已经更新(非确定性行为 - 请参阅eventual consistency了解更多详情,因为您没有使用Ancestor query保证strongly consistent结果; time.Sleep()延迟只会增加这种可能性。)

因此,数据存储区看到最后处理的实体不满足过滤器,并且不会再次搜索所有实体,但报告没有更多实体与过滤器匹配,因此不会更新更多实体(并且不会报告任何错误) )。

建议:不要使用游标,过滤或按照您同时更新的同一属性进行排序。

顺便说一下:

您引用的Appengine文档中的部分:

  

对于在具有多个值的属性上使用不等式过滤器或排序顺序的查询,游标并不总是按预期工作。

这不是你的想法。这意味着:游标可能无法在具有多个值的属性上正常工作并且相同的属性包含在不等式过滤器中用于按结果对结果进行排序。 / p>

顺便说一下#2

在屏幕截图中,您使用的是SDK 1.9.17。最新的SDK版本是1.9.21。您应该更新它并始终使用最新的可用版本。

实现目标的替代方案

1)不要使用游标

如果你有很多记录,你将无法在一个步骤中(在一个循环中)更新所有实体,但是假设您更新了300个实体。如果重复查询,则已更新的实体将不会再次执行相同查询的结果,因为更新的Value=2不满足过滤器Value<2。只需重做查询+更新,直到查询没有结果。由于您的更改为idempotent,因此如果实体的索引条目的更新被延迟并且查询将多次返回,则不会造成任何损害。最好延迟执行下一个查询以最大限度地减少这种情况(例如,在重做查询之间等待几秒钟)。

优点:简单。您已经有了解决方案,只需排除游标处理部分。

缺点:某些实体可能会多次更新(因此更改必须为idempotent)。此外,对实体执行的更改必须是将实体从下一个查询中排除的内容。

2)使用任务队列

您可以先执行仅密钥查询,然后将更新推迟到使用任务。您可以创建任务,假设将100个密钥传递给每个任务,并且任务可以按密钥加载实体并执行更新。这将确保每个实体只更新一次。由于涉及任务队列,此解决方案会有更大的延迟,但在大多数情况下这不是问题。

优点:没有重复的更新(因此更改可能不是非idempotent)。即使要执行的更改不会从下一个查询中排除实体(更一般),也可以工作。

缺点:更高的复杂性。更大的滞后/延迟。

3)使用Map-Reduce

您可以使用map-reduce框架/实用程序对许多实体进行大规模并行处理。不确定它是否已在Go中实现。

优点:并行执行,甚至可以处理数百万或数十亿个实体。在大型实体编号的情况下快得多。加上专业人员列出2)使用任务队列。

缺点:更高的复杂性。可能还没有Go。