SERVER-121533 Fix MaxKey range inclusion for migration_destination_manager (#51012)

GitOrigin-RevId: 9c14094e37cb0470cbb5abe293a0919792ded2a0
This commit is contained in:
Abdul Qadeer 2026-04-02 21:41:50 +03:00 committed by MongoDB Bot
parent 6f5d11312b
commit 722b13eef2
2 changed files with 43 additions and 5 deletions

View File

@ -202,4 +202,33 @@ function assertMigrationComplete(dbName, ns, coll, shard0DB, expectedCount) {
st.s.getDB(dbName).coll.drop();
}
// Test 8: Recipient pre-cloning check detects MaxKey orphans with wider-than-shard-key index.
// checkForExistingDocumentsInRange must extend bounds to the index width so that a MaxKey
// document already present on the recipient is detected even when the index is wider than
// the shard key.
{
const {dbName, ns, coll, shard0DB} = setupCollection("recipient_wider_idx", {x: 1});
assert.commandWorked(coll.createIndex({x: 1, y: 1}));
assert.commandWorked(coll.dropIndex({x: 1}));
assert.commandWorked(coll.insert({_id: "normal", x: 5}));
// Directly insert a MaxKey orphan on shard1 (bypassing mongos to simulate an orphan from
// a prior buggy migration that left it behind). The wider index {x:1, y:1} means the scan
// must use extendRangeBound to pad bounds to match the index width.
assert.commandWorked(st.shard1.getDB(dbName).coll.insert({_id: "orphan_maxkey", x: MaxKey(), y: 42}));
// Migrate the chunk [MinKey, MaxKey) from shard0 to shard1. The recipient's pre-cloning
// check should detect the orphaned MaxKey document and abort the migration. Without the
// extendRangeBound fix, the wider index scan would use {x: MaxKey, y: MinKey} as the
// upper bound, missing the orphan at {x: MaxKey, y: 42}.
const result = st.s.adminCommand({moveChunk: ns, find: {x: 0}, to: st.shard1.shardName, _waitForDelete: true});
assert.commandFailed(result);
// Clean up the orphan directly on shard1.
assert.commandWorked(st.shard1.getDB(dbName).coll.remove({_id: "orphan_maxkey"}));
coll.drop();
}
st.stop();

View File

@ -232,7 +232,7 @@ std::string stateToString(MigrationDestinationManager::State state) {
* Checks if an upsert of a remote document will override a local document with the same _id but in
* a different range on this shard. Must be in WriteContext to avoid races and DBHelper errors.
*
* TODO: Could optimize this check out if sharding on _id.
* TODO SERVER-123300: Could optimize this check out if sharding on _id.
*/
bool willOverrideLocalId(OperationContext* opCtx,
const NamespaceString& nss,
@ -2234,13 +2234,22 @@ boost::optional<BSONObj> MigrationDestinationManager::checkForExistingDocumentsI
<< " on collection " << nss.toStringForErrorMsg(),
shardKeyIdx);
// Use InternalPlanner to scan the shard key index within the range.
// Extend bounds to match the index width and strip field names for the index scan.
// When the range max is the global max (all fields MaxKey), use makeUpperInclusive=true
// so trailing fields are padded with MaxKey instead of MinKey, and use inclusive upper
// bound so the scan detects documents whose shard key is exactly MaxKey. See SERVER-121533.
const KeyPattern kp(shardKeyIdx->keyPattern());
const bool isMaxGlobal = kp.isGlobalMax(max);
const auto extendedMin = Helpers::toKeyFormat(kp.extendRangeBound(min, false));
const auto extendedMax = Helpers::toKeyFormat(kp.extendRangeBound(max, isMaxGlobal));
const auto boundInclusion = isMaxGlobal ? BoundInclusion::kIncludeBothStartAndEndKeys
: BoundInclusion::kIncludeStartKeyOnly;
auto exec = InternalPlanner::shardKeyIndexScan(opCtx,
collection,
*shardKeyIdx,
min,
max,
BoundInclusion::kIncludeStartKeyOnly,
extendedMin,
extendedMax,
boundInclusion,
PlanYieldPolicy::YieldPolicy::YIELD_AUTO,
InternalPlanner::FORWARD);