我有一个不寻常的mongodb游标超时问题 - 即使没有达到10分钟的超时,我收到cursor not found
错误。
我的环境:
这是一些代码。简而言之,我遍历数百万用户,为每个用户执行大型聚合,使用游标循环结果,然后运行一堆批量upsert。
<?php
/**
* THE AIM IS TO PERFORM A BIG AGGREGATION ON AN ENTIRE COLLECTION, MASSAGE THE RESULTS, AND WRITE INTO A PERMANENT COLLECTION.
*
* THE AGGREGATION IS BIG, SO IT WRITES TO DISK, AND WE ACCESS WITH A CURSOR.
*
* IN ORDER TO IMPROVE PERFORMANCE, WE BULK-UPSERT INTO THE PERMANENT COLLECTION ON EVERY 10k DOCUMENTS.
*
*/
class Analytics
{
const MAX_CURSOR_PAGE_SIZE = 10000;
public function performAnalytics($arr_user_ids)
{
foreach ($arr_user_ids as $item_user_id)
$this->performUserAnalytics($item_user_id->id);
}
private function performUserAnalytics($userId)
{
// AGGREGATION QUERY
$userItemsPage = $this->getUserItemsPage($userId);
// BULK UPSERT
$arrItemOps = [];
foreach ($userItemsPage as $item_user)
{
array_push($arrItemOps, [
'updateOne' => [
[ 'my_key' => $item_user->_id, ],
[ '$set' => [ 'item_ids' => $item_user->item_ids ] ],
[ 'upsert' => true ]
]
]);
// BULK-UPSERT FOR PERFORMANCE
if(count($arrItemOps) > self::MAX_CURSOR_PAGE_SIZE)
{
Log::info("BULK-UPSERTING USER ITEMS");
$permanent_collection->bulkUpsert($arrItemOps);
$arrItemOps = [];
}
}
// FINAL BULK-UPSERT
if(count($arrItemOps) > 0)
$permanent_collection->bulkUpsert($arrItemOps);
}
// BIG AGGREGATION
private function getUserItemsPage($userId)
{
return $items_collection->aggregate([
[
'$match' => [
'user_id' => $userId
]
],
[
'$group' => [
'_id' => '$user_id',
'item_ids' => [
'$addToSet' => '$item_id'
]
]
]
],
[ 'allowDiskUse' => true ]);
}
}
代码运行需要2天左右。 1.3天后,我在cursor not found
行上收到foreach ($userItemsPage as $item_user)
错误。
令我头疼的是我看到了:
BULK-UPSERTING用户项目
每隔50秒记录一次(45秒循环超过10k结果,5s结束批量翻转),然后在1分钟之后出现错误行。 每个用户分析需要1-120分钟。我没有修改101个文档的默认批量大小。
所以我很难看到超时错误隐藏的位置。
我在这里(MongoDB - Error: getMore command failed: Cursor not found)完成了@Danziger的优秀回复,但这似乎与我的情景无关。
还有另一个相关但未回答的问题(MongoDB - PHP - MongoCursorException 'Cursor not found')
谢谢,
答案 0 :(得分:0)
好的,我想我了解现在发生了什么,find()
和aggregate()
对batchSize
的行为与不同。
查找(https://docs.mongodb.com/manual/reference/method/cursor.batchSize):
指定每批中要返回的文档数
指定光标的初始批量大小
因此,对于aggregate()
,为返回的游标指定后续批处理的batchSize,而不是在聚合命令中。但是在PHP中这是不可能的 - 我们对游标对象没有相同级别的控制。这意味着无法在PHP中控制聚合batchSize。我和mongo(https://jira.mongodb.org/browse/PHPLIB-312)的好人一起为此提出了一张票。
在我的情况下,这表现为mongo给我一大批(约130k文件),可能需要> 10分钟来处理。我一次插入10k文件,但这并不意味着我一次只能从mongo读取10k批文件。