/** * Cleans up one range of orphaned data starting from a range that overlaps or starts at * 'startingFromKey'. If empty, startingFromKey is the minimum key of the sharded range. * * @return CleanupResult_Continue and 'stoppedAtKey' if orphaned range was found and cleaned * @return CleanupResult_Done if no orphaned ranges remain * @return CleanupResult_Error and 'errMsg' if an error occurred * * If the collection is not sharded, returns CleanupResult_Done. */ CleanupResult cleanupOrphanedData(OperationContext* txn, const NamespaceString& ns, const BSONObj& startingFromKeyConst, const WriteConcernOptions& secondaryThrottle, BSONObj* stoppedAtKey, string* errMsg) { BSONObj startingFromKey = startingFromKeyConst; CollectionMetadataPtr metadata = shardingState.getCollectionMetadata(ns.toString()); if (!metadata || metadata->getKeyPattern().isEmpty()) { warning() << "skipping orphaned data cleanup for " << ns.toString() << ", collection is not sharded" << endl; return CleanupResult_Done; } BSONObj keyPattern = metadata->getKeyPattern(); if (!startingFromKey.isEmpty()) { if (!metadata->isValidKey(startingFromKey)) { *errMsg = stream() << "could not cleanup orphaned data, start key " << startingFromKey << " does not match shard key pattern " << keyPattern; warning() << *errMsg << endl; return CleanupResult_Error; } } else { startingFromKey = metadata->getMinKey(); } KeyRange orphanRange; if (!metadata->getNextOrphanRange(startingFromKey, &orphanRange)) { LOG(1) << "orphaned data cleanup requested for " << ns.toString() << " starting from " << startingFromKey << ", no orphan ranges remain" << endl; return CleanupResult_Done; } orphanRange.ns = ns; *stoppedAtKey = orphanRange.maxKey; // We're done with this metadata now, no matter what happens metadata.reset(); LOG(1) << "orphaned data cleanup requested for " << ns.toString() << " starting from " << startingFromKey << ", removing next orphan range" << " [" << orphanRange.minKey << "," << orphanRange.maxKey << ")" << endl; // Metadata snapshot may be stale now, but deleter checks metadata again in write lock // before delete. RangeDeleterOptions deleterOptions(orphanRange); deleterOptions.writeConcern = secondaryThrottle; deleterOptions.onlyRemoveOrphanedDocs = true; deleterOptions.fromMigrate = true; // Must wait for cursors since there can be existing cursors with an older // CollectionMetadata. deleterOptions.waitForOpenCursors = true; deleterOptions.removeSaverReason = "cleanup-cmd"; if (!getDeleter()->deleteNow(txn, deleterOptions, errMsg)) { warning() << *errMsg << endl; return CleanupResult_Error; } return CleanupResult_Continue; }
/** * Cleans up one range of orphaned data starting from a range that overlaps or starts at * 'startingFromKey'. If empty, startingFromKey is the minimum key of the sharded range. * * @return CleanupResult_Continue and 'stoppedAtKey' if orphaned range was found and cleaned * @return CleanupResult_Done if no orphaned ranges remain * @return CleanupResult_Error and 'errMsg' if an error occurred * * If the collection is not sharded, returns CleanupResult_Done. */ CleanupResult cleanupOrphanedData( const NamespaceString& ns, const BSONObj& startingFromKeyConst, bool secondaryThrottle, BSONObj* stoppedAtKey, string* errMsg ) { BSONObj startingFromKey = startingFromKeyConst; CollectionMetadataPtr metadata = shardingState.getCollectionMetadata( ns.toString() ); if ( !metadata || metadata->getKeyPattern().isEmpty() ) { warning() << "skipping orphaned data cleanup for " << ns.toString() << ", collection is not sharded" << endl; return CleanupResult_Done; } BSONObj keyPattern = metadata->getKeyPattern(); if ( !startingFromKey.isEmpty() ) { if ( !metadata->isValidKey( startingFromKey ) ) { *errMsg = stream() << "could not cleanup orphaned data, start key " << startingFromKey << " does not match shard key pattern " << keyPattern; warning() << *errMsg << endl; return CleanupResult_Error; } } else { startingFromKey = metadata->getMinKey(); } KeyRange orphanRange; if ( !metadata->getNextOrphanRange( startingFromKey, &orphanRange ) ) { LOG( 1 ) << "orphaned data cleanup requested for " << ns.toString() << " starting from " << startingFromKey << ", no orphan ranges remain" << endl; return CleanupResult_Done; } *stoppedAtKey = orphanRange.maxKey; // We're done with this metadata now, no matter what happens metadata.reset(); LOG( 1 ) << "orphaned data cleanup requested for " << ns.toString() << " starting from " << startingFromKey << ", removing next orphan range" << " [" << orphanRange.minKey << "," << orphanRange.maxKey << ")" << endl; // Metadata snapshot may be stale now, but deleter checks metadata again in write lock // before delete. if ( !getDeleter()->deleteNow( ns.toString(), orphanRange.minKey, orphanRange.maxKey, keyPattern, secondaryThrottle, errMsg ) ) { warning() << *errMsg << endl; return CleanupResult_Error; } return CleanupResult_Continue; }