文档首页/ 文档数据库服务 DDS/ 最佳实践/ 避免hideIndex导致游标失效
更新时间:2024-12-27 GMT+08:00
分享

避免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));
	});
})();

相关文档