避免hideIndex导致游标失效
失效场景
在 DDS 中,某些索引操作可能导致游标失效,DDS这些行为和社区版MongoDB行为一致。以下是几个常见的例子及其解释:
hideIndex导致游标失效
原因:
hideIndex操作改变了索引的元数据,从而使现有游标失效。
示例代码:
(function() { "use strict"; let collName = "test_coll"; let coll = db.getCollection(collName); coll.drop(); // 创建索引,并插入数据, coll.createIndex({x: 1}, {background: false}); for (var i = 0; i < 1000; ++i) { coll.insert({x: i}); } // 获取游标 let cursor = coll.find({x: {$gte: 1}}); // 使用游标,但是游标并没有使用完 for (var i = 0; i < 101; ++i) { let doc = cursor.next(); print(tojson(doc)); } // 在游标使用完之前隐藏索引 coll.hideIndex("x_1"); // 遍历游标 cursor.forEach((doc) => { // 游标会报错: // "errmsg" : "definition of index 'x_1' changed" // "codeName" : "QueryPlanKilled" print(tojson(doc)); }); })();
结果:
游标失效,可能抛出错误。
2024-12-24T16:26:42.445+0800 E QUERY [js] Error: getMore command failed: { "ok" : 0, "errmsg" : "definition of index 'x_1' changed", "code" : 175, "codeName" : "QueryPlanKilled" } :
dropIndex 导致游标失效
原因:
删除索引后,DDS需要重新计算查询计划,从而使现有游标失效。
示例代码:
(function() { "use strict"; let collName = "test_coll"; let coll = db.getCollection(collName); coll.drop(); // 创建索引,并插入数据, coll.createIndex({x: 1}, {background: false}); for (var i = 0; i < 1000; ++i) { coll.insert({x: i}); } // 获取游标 let cursor = coll.find({x: {$gte: 1}}); // 使用游标,但是游标并没有使用完 for (var i = 0; i < 101; ++i) { let doc = cursor.next(); print(tojson(doc)); } // 在游标使用完之前删除索引 coll.dropIndex("x_1"); // 遍历游标 cursor.forEach((doc) => { // 游标会报错: // "errmsg" : "index 'x_1' dropped", // "codeName" : "QueryPlanKilled" print(tojson(doc)); }); })();
结果:
游标失效,因为删除索引会导致查询计划需要重新计算。
2024-12-25T10:34:29.948+0800 E QUERY [js] Error: getMore command failed: { "ok" : 0, "errmsg" : "index 'x_1' dropped", "code" : 175, "codeName" : "QueryPlanKilled" }
renameCollection导致游标失效
原因:
修改集合结构renameCollection也会导致游标失效。
示例代码:
(function() { "use strict"; let collName = "test_coll"; let coll = db.getCollection(collName); coll.drop(); // 创建索引,并插入数据, coll.createIndex({x: 1}, {background: false}); for (var i = 0; i < 1000; ++i) { coll.insert({x: i}); } // 获取游标 let cursor = coll.find({x: {$gte: 1}}); // 使用游标,但是游标并没有使用完 for (var i = 0; i < 101; ++i) { let doc = cursor.next(); print(tojson(doc)); } // 在游标使用完之前重命名集合 coll.renameCollection("test_coll_reaname"); // 遍历游标 cursor.forEach((doc) => { // 游标会报错: // "errmsg" : "collection dropped between getMore calls", // "codeName" : "OperationFailed" print(tojson(doc)); }); })();
结果:
游标失效,因为集合的名称改变,游标失去了上下文。
2024-12-25T10:39:28.624+0800 E QUERY [js] Error: getMore command failed: { "ok" : 0, "errmsg" : "collection dropped between getMore calls", "code" : 96, "codeName" : "OperationFailed" } :
dropCollection导致游标失效
原因:
删除集合游标失去了上下文,会导致游标失效。
示例代码:
(function() { "use strict"; let collName = "test_coll"; let coll = db.getCollection(collName); coll.drop(); // 创建索引,并插入数据, coll.createIndex({x: 1}, {background: false}); for (var i = 0; i < 1000; ++i) { coll.insert({x: i}); } // 获取游标 let cursor = coll.find({x: {$gte: 1}}); // 使用游标,但是游标并没有使用完 for (var i = 0; i < 101; ++i) { let doc = cursor.next(); print(tojson(doc)); } // 在游标使用完之前删除集合 coll.drop(); // 遍历游标 cursor.forEach((doc) => { // 游标会报错: // "errmsg" : "collection dropped between getMore calls", // "codeName" : "OperationFailed" print(tojson(doc)); }); })();
结果:
游标失效,因为集合已经被删除,游标失去了上下文。
2024-12-25T10:40:33.737+0800 E QUERY [js] Error: getMore command failed: { "ok" : 0, "errmsg" : "collection dropped between getMore calls", "code" : 96, "codeName" : "OperationFailed" } :
解决方法
- 在索引操作之前处理游标: 确保在 hideIndex 或 dropIndex 之前使用完游标。
- 重新获取游标: 如果索引发生变化,重新执行查询以获取新的游标。
- 提前计划: 避免在长时间查询中执行索引操作。
示例代码:
(function() { "use strict"; let collName = "test_coll"; let coll = db.getCollection(collName); coll.drop(); // 创建索引,并插入数据, coll.createIndex({x: 1}, {background: false}); for (var i = 0; i < 1000; ++i) { coll.insert({x: i}); } // 获取游标 let cursor = coll.find({x: {$gte: 1}}); // 使用游标,但是游标并没有使用完 for (var i = 0; i < 101; ++i) { let doc = cursor.next(); print(tojson(doc)); } // 在游标使用完之前隐藏索引 coll.hideIndex("x_1"); // 遍历游标 // cursor.forEach((doc) => { // 游标会报错: // "errmsg" : "definition of index 'x_1' changed" // "codeName" : "QueryPlanKilled" // print(tojson(doc)); // }); // 重新获取游标 cursor = coll.find({x: {$gte: 1}}); cursor.forEach((doc) => { print(tojson(doc)); }); })();