How do I clean up orphaned documents?
Orphaned documents in a MongoDB sharded cluster can degrade performance and cause errors during instance migration or when releasing shard nodes. Check for and clean up orphaned documents before performing either operation.
Prerequisites
Before you begin, ensure that you have:
-
The mongo shell installed on your server, with the server connected to the ApsaraDB for MongoDB sharded cluster instance. For connection instructions, see Connect to an ApsaraDB for MongoDB sharded cluster instance by using the mongo shell
-
An instance running MongoDB 4.2 or later (major version) and MongoDB 4.0.6 or later (minor version). Running the cleanup script on earlier versions causes an error. To upgrade, see Upgrade the major version of an ApsaraDB for MongoDB instance and Update the minor version of an ApsaraDB for MongoDB instance
The examples in this topic use the mongo shell. Other connection tools may produce different output formats.
Check for orphaned documents
Choose the method that matches your instance's MongoDB version.
Instances running MongoDB 5.0 or earlier
Connect to the mongos node using the root role or a privileged account, then run the following statement on each collection you want to check:
db.getSiblingDB("<db>").<collection>.find().readPref("secondary").readConcern("local").explain("executionStats")
Replace the placeholders with your values:
| Placeholder | Description |
|---|---|
<db> |
The name of the MongoDB database |
<collection> |
The name of the collection |
-
This statement scans all documents across all shard nodes. The
readPreferenceparameter is set tosecondaryto route execution to secondary nodes and reduce impact on your workload. -
Execution time scales with data volume. For large collections, wait until the results are returned.
-
Run the statement once per collection.
In the results, focus on the SHARDING_FILTER stage. The chunkSkips field shows the number of orphaned documents in that collection on the current shard node.
n},
{
"shardName" : "xxx",
"executionSuccess" : true,
"executionStages" : {
"stage" : "SHARDING_FILTER",
"nReturned" : 31186672,
"executionTimeMillisEstimate" : 1761,
"works" : 34711383,
"advanced" : 31186672,
"needTime" : 3524710,
"needYield" : 0,
"saveState" : 271182,
"restoreState" : 271182,
"isEOF" : 1,
"chunkSkips" : 3524709,
"inputStage" : {
"stage" : "COLLSCAN",
"nReturned" : 34711381,
"executionTimeMillisEstimate" : 658,
"works" : 34711383,
"advanced" : 34711381,
"needTime" : 1,
"needYield" : 0,
"saveState" : 271182,
"restoreState" : 271182,
"isEOF" : 1,
"direction" : "forward",
"docsExamined" : 34711381,
"docsExaminedBySizeInBytes" : NumberLong("14316055356")
}
}
}
]
},
"allPlansExecution" : [
{
"shardName" : "xxx",
"allPlans" : [ ]
Instances running MongoDB 6.0 or later
This method uses the $shardedDataDistribution aggregation stage, which requires MongoDB 7.0.1 (minor version) or later (benchmark version: 6.0.3). If your instance runs an earlier minor version, update it to the latest version first. For instructions, see Update the minor version of an ApsaraDB for MongoDB instance.
If you cannot update the minor version, use the MongoDB 5.0 or earlier method.
Connect to the mongos node using the root role or a privileged account, then run one of the following statements:
// Check all namespaces in the sharded cluster
db.getSiblingDB("admin").aggregate( [{ $shardedDataDistribution: { } }] ).pretty()
// Check a single namespace
db.getSiblingDB("admin").aggregate( [{ $shardedDataDistribution: { } },{ $match: { "ns": "<db>.<collection>" } }] ).pretty()
The output shows the data distribution across all shards, including orphaned document counts. Example output:
[
{
"ns": "test.names",
"shards": [
{
"shardName": "shard-1",
"numOrphanedDocs": 0,
"numOwnedDocuments": 6,
"ownedSizeBytes": 366,
"orphanedSizeBytes": 0
},
{
"shardName": "shard-2",
"numOrphanedDocs": 0,
"numOwnedDocuments": 6,
"ownedSizeBytes": 366,
"orphanedSizeBytes": 0
}
]
}
]
If numOrphanedDocs is not 0 for any shard, proceed to clean up orphaned documents. For field descriptions, see the MongoDB documentation.
Clean up orphaned documents
The cleanup method differs between ApsaraDB for MongoDB instances and self-managed MongoDB databases.
An error is reported when you run a cleanup script in an ApsaraDB for MongoDB instance whose major version is earlier than MongoDB 4.2 and whose minor version is earlier than MongoDB 4.0.6. For more information about how to view the version of an ApsaraDB for MongoDB instance, see Release notes for the minor versions of ApsaraDB for MongoDB. For more information about how to upgrade the major version of an ApsaraDB for MongoDB instance and update the minor version of the instance, see Upgrade the major version of an ApsaraDB for MongoDB instance and Update the minor version of an ApsaraDB for MongoDB instance.
ApsaraDB for MongoDB instances
Step 1: Create the cleanup script
Create a JavaScript script named cleanupOrphaned.js on a server that can connect to the sharded cluster instance. Use the script that matches your MongoDB version.
MongoDB 4.4 or later
This script cleans orphaned documents from all collections across multiple databases and shard nodes.
To clean a specific collection only, modify the databasesToProcess and collection filtering logic in the script.
// The IDs of the shard nodes to clean
var shardNames = ["shardName1", "shardName2"];
// The databases to clean
var databasesToProcess = ["database1", "database2", "database3"];
shardNames.forEach(function(shardName) {
// Traverse the specified databases
databasesToProcess.forEach(function(dbName) {
var dbInstance = db.getSiblingDB(dbName);
// Get all collection names in the database
var collectionNames = dbInstance.getCollectionNames();
// Traverse all collections
collectionNames.forEach(function(collectionName) {
// Build the full collection name
var fullCollectionName = dbName + "." + collectionName;
// Build the cleanupOrphaned command
var command = {
runCommandOnShard: shardName,
command: { cleanupOrphaned: fullCollectionName }
};
// Run the cleanupOrphaned command
var result = db.adminCommand(command);
printjson(result);
if (result.ok) {
print("Cleaned up orphaned documents for collection " + fullCollectionName + " on shard " + shardName);
} else {
print("Failed to clean up orphaned documents for collection " + fullCollectionName + " on shard " + shardName);
}
});
});
});
Replace the following parameters in the script:
| Parameter | Description |
|---|---|
shardNames |
The IDs of the shard nodes to clean. Find these IDs in the Shard List section on the Basic Information page of the instance. Example: d-bp15a3796d3a**** |
databasesToProcess |
The names of the databases to clean |
MongoDB 4.2 and earlier
This script cleans orphaned documents from a single collection across multiple shard nodes.
To clean multiple collections, modify the fullCollectionName parameter and repeat the script for each collection, or extend the script to traverse all collections.
function cleanupOrphanedOnShard(shardName, fullCollectionName) {
var nextKey = { };
var result;
while ( nextKey != null ) {
var command = {
runCommandOnShard: shardName,
command: { cleanupOrphaned: fullCollectionName, startingFromKey: nextKey }
};
result = db.adminCommand(command);
printjson(result);
if (result.ok != 1 || !(result.results.hasOwnProperty(shardName)) || result.results[shardName].ok != 1 ) {
print("Unable to complete at this time: failure or timeout.")
break
}
nextKey = result.results[shardName].stoppedAtKey;
}
print("cleanupOrphaned done for coll: " + fullCollectionName + " on shard: " + shardName)
}
var shardNames = ["shardName1", "shardName2", "shardName3"]
var fullCollectionName = "database.collection"
shardNames.forEach(function(shardName) {
cleanupOrphanedOnShard(shardName, fullCollectionName);
});
Replace the following parameters in the script:
| Parameter | Description |
|---|---|
shardNames |
The IDs of the shard nodes to clean. Find these IDs in the Shard List section on the Basic Information page of the instance. Example: d-bp15a3796d3a**** |
fullCollectionName |
The collection to clean, in database.collection format |
Step 2: Run the cleanup script
Run cleanupOrphaned.js using either of the following methods.
Method 1: Run directly from the mongo shell command
From the directory where cleanupOrphaned.js is stored, run:
mongo --host <Mongoshost> --port <Primaryport> --authenticationDatabase <database> -u <username> -p <password> cleanupOrphaned.js > output.txt
| Parameter | Description |
|---|---|
<Mongoshost> |
The endpoint of the mongos node. Format: s-bp14423a2a51****.mongodb.rds.aliyuncs.com |
<Primaryport> |
The port of the mongos node. Default: 3717 |
<database> |
The authentication database for the account |
<username> |
The database account |
<password> |
The account password |
output.txt |
The file where execution results are saved |
Method 2: Load the script from inside a connected session
Connect to the instance using the mongo shell. For connection instructions, see Connect to an ApsaraDB for MongoDB sharded cluster instance by using the mongo shell. Then, from the directory where the script is stored, run:
load("cleanupOrphaned.js")mongosh "mongodb://root:xxx@xxx.mongodb.rds.aliyuncs.com:3717" --authenticationDatabase admin
Current Mongosh Log ID: 65b213b61c3b76ba0648a859
Connecting to: mongodb://<credentials>@xxx.mongodb.rds.aliyuncs.com:3717/?directConnection=true&authSource=admin&appName=mongosh.1
Using MongoDB: 4.2.23
Using Mongosh: 1.6.1
For mongosh info see: https://docs.mongodb.com/mongodb-shell/
[direct: mongos] test> load("cleanupOrphaned.js")
{
results: {
'xxx.xxx': {
ok: 1,
'$gleStats': {
lastOpTime: {
ts: Timestamp({ t: 1706169270, i: 1 }),
t: Long("1")
},
electionId: ObjectId("7fffffff0000000000000001")
},
lastCommittedOpTime: Timestamp({ t: 1706169270, i: 1 }),
'$configServerState': {
opTime: {
ts: Timestamp({ t: 1706169272, i: 1 }),
Self-managed MongoDB databases
-
Download the
cleanupOrphaned.jsscript:wget "https://docs-aliyun.cn-hangzhou.oss.aliyun-inc.com/assets/attach/120562/cn_zh/1564451237979/cleanupOrphaned.js" -
Open
cleanupOrphaned.jsand replacetestwith the name of the database to clean.ImportantTo clean multiple databases, repeat steps 2 and 3 for each database.
function cleanupOrphaned(coll) { var nextKey = { }; var result; while ( nextKey != null ) { result = db.adminCommand( { cleanupOrphaned: coll, startingFromKey: nextKey } ); if (result.ok != 1) print("Unable to complete at this time: failure or timeout.") printjson(result); nextKey = result.stoppedAtKey; } } var dbName = 'test' db = db.getSiblingDB(dbName) db.getCollectionNames().forEach(function(collName) { cleanupOrphaned(dbName + "." + collName); }); -
Run the following command on each shard node:
NoteRepeat this step on every shard node.
Parameter Description <Shardhost>The IP address of the shard node <Primaryport>The service port of the primary node in the shard <database>The authentication database for the account <username>The database account <password>The account password mongo --host <Shardhost> --port <Primaryport> --authenticationDatabase <database> -u <username> -p <password> cleanupOrphaned.jsExample: A self-managed cluster with three shard nodes:
mongo --host 172.16.1.10 --port 27018 --authenticationDatabase admin -u dtstest -p 'Test123456' cleanupOrphaned.js mongo --host 172.16.1.11 --port 27021 --authenticationDatabase admin -u dtstest -p 'Test123456' cleanupOrphaned.js mongo --host 172.16.1.12 --port 27024 --authenticationDatabase admin -u dtstest -p 'Test123456' cleanupOrphaned.js
References
-
cleanupOrphanedcommand reference: