Help Center/ Document Database Service/ Best Practices/ Avoiding Cursor Invalidity Caused by hideIndex
Updated on 2025-05-30 GMT+08:00

Avoiding Cursor Invalidity Caused by hideIndex

Scenarios

In DDS, some operations may cause cursors to be invalid. The behaviors of DDS are the same as those of MongoDB Community Edition. The following are some common examples:

Invalid cursors caused by hideIndex

Cause:

The hideIndex operation changes the metadata of an index, invalidating the existing cursor.

Sample code:

(function() {
	"use strict";

	let collName = "test_coll";
	let coll = db.getCollection(collName);
	coll.drop();

        // Create an index and insert data.
	coll.createIndex({x: 1}, {background: false});
	for (var i = 0; i < 1000; ++i) {
		coll.insert({x: i});
	}

        // Obtain a cursor.
	let cursor = coll.find({x: {$gte: 1}});

        // Use the cursor, but the cursor is not exhausted.
	for (var i = 0; i < 101; ++i) {
		let doc = cursor.next();
		print(tojson(doc));
	}

        // Hide an index before the cursor is exhausted.
	coll.hideIndex("x_1");

        // Traverse the cursor.
	cursor.forEach((doc) => {
                  // An error is reported for the cursor.
		// "errmsg" : "definition of index 'x_1' changed"
		// "codeName" : "QueryPlanKilled"
		print(tojson(doc));
	});
})();

Result:

The cursor is invalid. Errors may be reported.

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"
} :

Invalid cursors caused by dropIndex

Cause:

After an index is deleted, DDS needs to recalculate query plans, invalidating the existing cursor.

Sample code:

(function() {
	"use strict";

	let collName = "test_coll";
	let coll = db.getCollection(collName);
	coll.drop();

        // Create an index and insert data.
	coll.createIndex({x: 1}, {background: false});
	for (var i = 0; i < 1000; ++i) {
		coll.insert({x: i});
	}

        // Obtain a cursor.
	let cursor = coll.find({x: {$gte: 1}});

        // Use the cursor, but the cursor is not exhausted.
	for (var i = 0; i < 101; ++i) {
		let doc = cursor.next();
		print(tojson(doc));
	}

        // Delete an index before the cursor is exhausted.
	coll.dropIndex("x_1");

        // Traverse the cursor.
	cursor.forEach((doc) => {
                  // An error is reported for the cursor.
		// "errmsg" : "index 'x_1' dropped",
		// "codeName" : "QueryPlanKilled"
		print(tojson(doc));
	});
})();

Result:

The cursor is invalid because query plans need to be recalculated after an index is deleted.

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"
}

Invalid cursors caused by renameCollection

Cause:

The renameCollection operation also causes cursors to become invalid.

Sample code:

(function() {
	"use strict";

	let collName = "test_coll";
	let coll = db.getCollection(collName);
	coll.drop();

        // Create an index and insert data.
	coll.createIndex({x: 1}, {background: false});
	for (var i = 0; i < 1000; ++i) {
		coll.insert({x: i});
	}

        // Obtain a cursor.
	let cursor = coll.find({x: {$gte: 1}});

        // Use the cursor, but the cursor is not exhausted.
	for (var i = 0; i < 101; ++i) {
		let doc = cursor.next();
		print(tojson(doc));
	}

        // Rename a collection before the cursor is exhausted.
	coll.renameCollection("test_coll_reaname");

        // Traverse the cursor.
	cursor.forEach((doc) => {
                  // An error is reported for the cursor.
		// "errmsg" : "collection dropped between getMore calls",
		// "codeName" : "OperationFailed"
		print(tojson(doc));
	});
})();

Result:

The cursor is invalid because the name of the collection is changed and the cursor loses the context.

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"
} :

Invalid cursors caused by dropCollection

Cause:

Deleting a collection causes cursors to lose the context. As a result, cursors become invalid.

Sample code:

(function() {
	"use strict";

	let collName = "test_coll";
	let coll = db.getCollection(collName);
	coll.drop();

        // Create an index and insert data.
	coll.createIndex({x: 1}, {background: false});
	for (var i = 0; i < 1000; ++i) {
		coll.insert({x: i});
	}

        // Obtain a cursor.
	let cursor = coll.find({x: {$gte: 1}});

        // Use the cursor, but the cursor is not exhausted.
	for (var i = 0; i < 101; ++i) {
		let doc = cursor.next();
		print(tojson(doc));
	}

        // Delete a collection before the cursor is exhausted.
	coll.drop();

        // Traverse the cursor.
	cursor.forEach((doc) => {
                  // An error is reported for the cursor.
		// "errmsg" : "collection dropped between getMore calls",
		// "codeName" : "OperationFailed"
		print(tojson(doc));
	});
})();

Result:

The cursor is invalid because the collection is deleted and the cursor loses the context.

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"
} :

Solution

  • Process cursors before an index operation: Ensure that a cursor is exhausted before the hideIndex or dropIndex operation.
  • Re-obtain cursors: If an index changes, perform the query operation again to obtain the new cursor.
  • Plan in advance: Do not perform index operations during long-time queries.

Sample code:

(function() {
	"use strict";

	let collName = "test_coll";
	let coll = db.getCollection(collName);
	coll.drop();

        // Create an index and insert data.
	coll.createIndex({x: 1}, {background: false});
	for (var i = 0; i < 1000; ++i) {
		coll.insert({x: i});
	}

        // Obtain a cursor.
	let cursor = coll.find({x: {$gte: 1}});

        // Use the cursor, but the cursor is not exhausted.
	for (var i = 0; i < 101; ++i) {
		let doc = cursor.next();
		print(tojson(doc));
	}

        // Hide an index before the cursor is exhausted.
	coll.hideIndex("x_1");

        // Traverse the cursor.
	// cursor.forEach((doc) => {
                  // An error is reported for the cursor.
		// "errmsg" : "definition of index 'x_1' changed"
		// "codeName" : "QueryPlanKilled"
		// print(tojson(doc));
	// });

        // Obtain the cursor again.
	cursor = coll.find({x: {$gte: 1}});
	cursor.forEach((doc) => {
		print(tojson(doc));
	});
})();