我在golang上遇到数据存储查询的一些奇怪问题。查询执行正常但是当涉及拉动总数(cli.Count())时,它每次都会通过超时失败。有人可以解释我做错了什么或者是否有正确的方法来做到这一点?奇怪的部分只是它在查询计数上失败而且我不明白为什么,我试图创建一个NewQuery并初始化一个新的客户端但是都不起作用。
// GetListByID provides an iterative list of all items filtered by ID.
func GetListByID(theID, limit, offset int) ([]Item, int, error) {
var itemList []Item
cli, ctx, err := getClient()
if err != nil {
return itemList, 0, err
}
if limit <= 0 {
limit = 20
}
q := datastore.NewQuery("SomeKind").Filter("MasterID = ", theID)
ql := q.Limit(limit).Offset(offset)
_, err = cli.GetAll(ctx, ql, &itemList)
if err != nil {
return itemList, 0, err
}
if len(itemList) < limit && offset == 0 {
return itemList, len(itemList), nil
}
total, err := cli.Count(ctx, q)
return itemList, total, err
}
请注意,它不会在appengine上运行。它在AWS和数据中心的实例上运行。 (不要问)
答案 0 :(得分:1)
注意:Cloud Datastore没有服务器端count
聚合。这一点很重要,因为它会影响客户端实现count
等函数的方式。
total, err := cli.Count(ctx, q)
不会从您已执行的查询返回计数,它会将您的查询设为仅限密钥,然后发出新的调用以检索所有这些密钥。然后根据需要使用游标进行分页并计算所有结果。
您可以看到implementation here on gitHub
偏移 - 与游标(您应该切换到使用游标)不同,偏移不会仅仅从上次查询中断的位置恢复。要处理偏移量,Cloud Datastore需要手动计算与您的查询匹配的offset
个实体数,然后恢复。这意味着您的函数的每次调用都会在处理新的实体之前重新处理所有正在进行的实体 - 经典的O(N ^ 2)算法。
再次:切换到游标:D
这实际上与你获得超时的原因有关(或者,在这种情况下是无限循环)。 Count
的实现保留了偏移号。根据你拥有的实体数量以及你如何调用你的函数,这可能会非常大。
这导致我们得出最终答案:
撰写此问题时可用的Go客户端已损坏。我只是查看了提交历史记录,发现它没有处理偏移并且直到最近才正确跳过结果。这将导致它不断向Cloud Datastore发出一个查询,该查询没有取得进展(只是试图跳过偏移的第一个实体)。计数函数将继续返回,因为它继续调用nextbatch()
,从未取得进展。
这是在6月初通过以下提交修复的:https://github.com/GoogleCloudPlatform/gcloud-golang/commit/0bf7a0795c591c70a17505c1123bc3ef3d30f426
如果您获得了Go客户端的新版本,则不应再出现此无限循环问题。
答案 1 :(得分:0)
答案实际上正在按照我的预期进行,最近Okdave在github上解决了一个错误(我解决了一个问题),链接可以在这里找到:https://github.com/GoogleCloudPlatform/gcloud-golang/issues/268。我很抱歉忘记回到这里更新答案,我很感激你的细节,因为它解释了许多人可能会错过的很多东西。根据我的需要,我不想使用游标,因为它会破坏实现的功能。我期待选择项目50-100的某些内容,当再次执行查询时,第二次项目50-100不应与在第二次查询之前插入到数据库中的其他项目相同。有理由使用游标,有理由不...在我的情况下,我不想要它。问题与计数有关,虽然已经修复了。