SERVER-125916 SERVER-104976 Remove the repairShardedCollectionChunksHistory command (#53105)

GitOrigin-RevId: 5d1fc8602e0ad82e094d1a94c249473978309cde
This commit is contained in:
Pol Piñol Castuera 2026-05-14 11:14:24 +02:00 committed by MongoDB Bot
parent 13c8d307d8
commit c73dc85799
27 changed files with 2 additions and 581 deletions

View File

@ -98,7 +98,6 @@ resmoke_suite_test(
name = "no_passthrough",
srcs = [
"//jstests/noPassthrough/ddl:set_allow_migrations.js",
"//jstests/noPassthrough/global_catalog:repair_sharded_collection_history.js",
"//jstests/noPassthrough/global_catalog:reset_placement_history.js",
"//jstests/noPassthrough/versioning_protocol:_flush_cache_update_commands.js",
"//jstests/noPassthrough/versioning_protocol:flush_router_config.js",

View File

@ -252,10 +252,6 @@ const internalCommandsMap = {
testname: "_configsvrRemoveTags",
command: {_configsvrRemoveTags: "test"},
},
_configsvrRepairShardedCollectionChunksHistory: {
testname: "_configsvrRepairShardedCollectionChunksHistory",
command: {_configsvrRepairShardedCollectionChunksHistory: ns},
},
_configsvrResetPlacementHistory: {
testname: "_configsvrResetPlacementHistory",
command: {_configsvrResetPlacementHistory: ns},

View File

@ -292,7 +292,6 @@ const skippedAuthTestingCommands = [
"reapLogicalSessionCacheNow",
"recreateRangeDeletionTasks",
"releaseMemory",
"repairShardedCollectionChunksHistory",
"replicateSearchIndexCommand",
"replSetAbortPrimaryCatchUp",
"replSetRequestVotes",

View File

@ -118,7 +118,6 @@ let viewsCommandTests = {
_configsvrRemoveShard: {skip: isAnInternalCommand},
_configsvrRemoveShardFromZone: {skip: isAnInternalCommand},
_configsvrRemoveTags: {skip: isAnInternalCommand},
_configsvrRepairShardedCollectionChunksHistory: {skip: isAnInternalCommand},
_configsvrResetPlacementHistory: {skip: isAnInternalCommand},
_configsvrReshardCollection: {skip: isAnInternalCommand},
_configsvrRunRestore: {skip: isAnInternalCommand},
@ -665,13 +664,6 @@ let viewsCommandTests = {
skipSharded: true,
},
],
repairShardedCollectionChunksHistory: {
command: {repairShardedCollectionChunksHistory: "test.view"},
skipStandalone: true,
isAdminCommand: true,
expectFailure: true,
expectedErrorCode: ErrorCodes.ConflictingOperationInProgress,
},
replicateSearchIndexCommand: {skip: isAnInternalCommand},
replSetAbortPrimaryCatchUp: {skip: isUnrelated},
replSetFreeze: {skip: isUnrelated},

View File

@ -113,7 +113,6 @@ const wcCommandsTests = {
_configsvrRemoveShardFromZone: {skip: "internal command"},
_configsvrRemoveTags: {skip: "internal command"},
_configsvrRenameCollection: {skip: "internal command"},
_configsvrRepairShardedCollectionChunksHistory: {skip: "internal command"},
_configsvrResetPlacementHistory: {skip: "internal command"},
_configsvrReshardCollection: {skip: "internal command"},
_configsvrRunRestore: {skip: "internal command"},
@ -2242,7 +2241,6 @@ const wcCommandsTests = {
admin: true,
},
},
repairShardedCollectionChunksHistory: {skip: "does not accept write concern"},
replicateSearchIndexCommand: {skip: "internal command for testing only"},
replSetAbortPrimaryCatchUp: {skip: "does not accept write concern"},
replSetFreeze: {skip: "does not accept write concern"},
@ -3337,7 +3335,6 @@ const wcTimeseriesCommandsTests = {
_configsvrRemoveShardFromZone: {skip: "internal command"},
_configsvrRemoveTags: {skip: "internal command"},
_configsvrRenameCollection: {skip: "internal command"},
_configsvrRepairShardedCollectionChunksHistory: {skip: "internal command"},
_configsvrResetPlacementHistory: {skip: "internal command"},
_configsvrReshardCollection: {skip: "internal command"},
_configsvrRunRestore: {skip: "internal command"},
@ -4326,7 +4323,6 @@ const wcTimeseriesCommandsTests = {
removeShardFromZone: {skip: "does not accept write concern"},
// TODO SERVER-125423: add test coverage now that viewless timeseries are enabled.
renameCollection: {skip: "not supported on timeseries views"},
repairShardedCollectionChunksHistory: {skip: "does not accept write concern"},
replicateSearchIndexCommand: {skip: "internal command for testing only"},
replSetAbortPrimaryCatchUp: {skip: "does not accept write concern"},
replSetFreeze: {skip: "does not accept write concern"},

View File

@ -1,53 +0,0 @@
import {ShardingTest} from "jstests/libs/shardingtest.js";
let st = new ShardingTest({
shards: 1,
});
let shardPrimary = st.rs0.getPrimary();
let shardPrimaryConfigDB = shardPrimary.getDB("config");
let testDB = st.s.getDB("test1");
// Create a sharded collection with primary shard 0.
assert.commandWorked(st.s.adminCommand({enableSharding: testDB.getName(), primaryShard: st.shard0.shardName}));
assert.commandWorked(st.s.adminCommand({shardCollection: testDB.foo.getFullName(), key: {a: 1}}));
assert.commandWorked(st.s.adminCommand({split: testDB.foo.getFullName(), middle: {a: 0}}));
assert.commandWorked(st.s.adminCommand({split: testDB.foo.getFullName(), middle: {a: -1000}}));
assert.commandWorked(st.s.adminCommand({split: testDB.foo.getFullName(), middle: {a: +1000}}));
assert.writeOK(st.s.getDB("test1").foo.insert({_id: "id1", a: 1}));
assert.neq(null, st.s.getDB("test1").foo.findOne({_id: "id1", a: 1}));
assert.writeOK(st.s.getDB("test1").foo.insert({_id: "id2", a: -1}));
assert.neq(null, st.s.getDB("test1").foo.findOne({_id: "id2", a: -1}));
const collection = st.s.getDB("config").getCollection("collections").findOne({_id: "test1.foo"});
// Manually clear the 'historyIsAt40' field from the config server and the history entries from
// the shards' cache collections in order to simulate a wrong upgrade due to SERVER-62065
assert.writeOK(
st.s.getDB("config").chunks.update({uuid: collection.uuid}, {"$unset": {historyIsAt40: ""}}, {multi: true}),
);
assert.writeOK(
shardPrimaryConfigDB.getCollection("cache.chunks.test1.foo").update({}, {"$unset": {history: ""}}, {multi: true}),
);
assert.commandWorked(st.s.adminCommand({repairShardedCollectionChunksHistory: "test1.foo"}));
// Make sure chunks for test1.foo were given history after repair
let chunks = st.s.getDB("config").getCollection("chunks").find({uuid: collection.uuid}).toArray();
assert.eq(chunks.length, 4);
chunks.forEach((chunk) => {
assert.neq(null, chunk);
assert(chunk.hasOwnProperty("history"), "test1.foo does not have a history after repair");
assert(chunk.hasOwnProperty("historyIsAt40"), "test1.foo does not have a historyIsAt40 after repair");
});
chunks = shardPrimaryConfigDB.getCollection("cache.chunks.test1.foo").find().toArray();
assert.eq(chunks.length, 4);
chunks.forEach((chunk) => {
assert.neq(null, chunk);
assert(chunk.hasOwnProperty("history"), "test1.foo does not have a history on the shard after repair");
});
st.stop();

View File

@ -288,7 +288,6 @@ const allCommands = {
command: {renameCollection: fullNs, to: dbName + ".renamedColl"},
isAdminCommand: true,
},
repairShardedCollectionChunksHistory: {skip: isAnInternalCommand},
replicateSearchIndexCommand: {skip: isAnInternalCommand},
replSetGetStatus: {skip: "mongos does not run replset commands"},
resetPlacementHistory: {command: {resetPlacementHistory: 1}, isAdminCommand: true},

View File

@ -64,7 +64,6 @@ const allCommands = {
_configsvrRemoveShard: {skip: isAnInternalCommand},
_configsvrRemoveShardFromZone: {skip: isAnInternalCommand},
_configsvrRemoveTags: {skip: isAnInternalCommand},
_configsvrRepairShardedCollectionChunksHistory: {skip: isAnInternalCommand},
_configsvrResetPlacementHistory: {skip: isAnInternalCommand},
_configsvrReshardCollection: {skip: isAnInternalCommand},
_configsvrRunRestore: {skip: isAnInternalCommand},
@ -1337,7 +1336,6 @@ const allCommands = {
assert.commandWorked(conn.getDB(dbName).runCommand({drop: collName + "2"}));
},
},
repairShardedCollectionChunksHistory: {skip: isAnInternalCommand},
replicateSearchIndexCommand: {skip: isAnInternalCommand},
replSetAbortPrimaryCatchUp: {
// This will be tested in FCV upgrade/downgrade passthroughs through the replsets directory.

View File

@ -55,7 +55,6 @@ const allCommands = {
_configsvrRemoveShard: {skip: isPrimaryOnly},
_configsvrRemoveShardFromZone: {skip: isPrimaryOnly},
_configsvrRemoveTags: {skip: isPrimaryOnly},
_configsvrRepairShardedCollectionChunksHistory: {skip: isPrimaryOnly},
_configsvrResetPlacementHistory: {skip: isPrimaryOnly},
_configsvrReshardCollection: {skip: isPrimaryOnly},
_configsvrRunRestore: {skip: isPrimaryOnly},
@ -405,7 +404,6 @@ const allCommands = {
reIndex: {skip: isNotAUserDataRead},
releaseMemory: {skip: isNotAUserDataRead},
renameCollection: {skip: isPrimaryOnly},
repairShardedCollectionChunksHistory: {skip: isPrimaryOnly},
replSetAbortPrimaryCatchUp: {skip: isNotAUserDataRead},
replSetFreeze: {skip: isNotAUserDataRead},
replSetGetConfig: {skip: isNotAUserDataRead},

View File

@ -59,7 +59,6 @@ const allCommands = {
_configsvrRemoveShard: {skip: isAnInternalCommand},
_configsvrRemoveShardFromZone: {skip: isAnInternalCommand},
_configsvrRemoveTags: {skip: isAnInternalCommand},
_configsvrRepairShardedCollectionChunksHistory: {skip: isAnInternalCommand},
_configsvrResetPlacementHistory: {skip: isAnInternalCommand},
_configsvrReshardCollection: {skip: isAnInternalCommand},
_configsvrRunRestore: {skip: isAnInternalCommand},
@ -1045,7 +1044,6 @@ const allCommands = {
assert.commandWorked(mongoS.getDB(dbName).runCommand({drop: collName}));
},
},
repairShardedCollectionChunksHistory: {skip: isAnInternalCommand},
replicateSearchIndexCommand: {skip: isAnInternalCommand},
replSetAbortPrimaryCatchUp: {skip: "tested in direct_shard_connection_auth_rs_commands.js"},
replSetFreeze: {skip: "tested in direct_shard_connection_auth_rs_commands.js"},

View File

@ -873,7 +873,6 @@ const allTestCases = {
},
},
},
repairShardedCollectionChunksHistory: {skip: "always targets the config server"},
replicateSearchIndexCommand: {skip: "internal command for testing only"},
replSetGetStatus: {skip: "not supported in mongos"},
resetPlacementHistory: {skip: "always targets the config server"},
@ -1031,7 +1030,6 @@ const allTestCases = {
_configsvrRemoveShard: {skip: "TODO"},
_configsvrRemoveShardFromZone: {skip: "TODO"},
_configsvrRemoveTags: {skip: "TODO"},
_configsvrRepairShardedCollectionChunksHistory: {skip: "TODO"},
_configsvrResetPlacementHistory: {skip: "TODO"},
_configsvrReshardCollection: {skip: "TODO"},
_configsvrRunRestore: {skip: "TODO"},

View File

@ -8,6 +8,7 @@ export const commandsRemovedFromMongodSinceLastLTS = [
"_shardsvrCommitToShardLocalCatalog", // Removed in 8.2
"stageDebug",
"_configsvrRemoveShardCommit",
"_configsvrRepairShardedCollectionChunksHistory", // Removed in 9.0
"_configsvrAddShardCoordinator",
"_shardsvrChangePrimary", // Removed in 9.0
"_shardsvrCommitIndexParticipant",

View File

@ -6,6 +6,7 @@
export const commandsRemovedFromMongosSinceLastLTS = [
"_getAuditConfigGeneration", // Removed in 8.1
"changePrimary", // Removed in 9.0
"repairShardedCollectionChunksHistory", // Removed in 9.0
"startRecordingTraffic",
"stopRecordingTraffic",
"cleanupReshardCollection",

View File

@ -333,21 +333,6 @@ export let MongosAPIParametersUtil = (function () {
{commandName: "oidcListKeys", skip: "TODO(SERVER-108802)", conditional: true},
{commandName: "oidcRefreshKeys", skip: "TODO(SERVER-108802)", conditional: true},
{commandName: "removeQuerySettings", skip: "TODO(SERVER-108802)"},
{
commandName: "repairShardedCollectionChunksHistory",
run: {
inAPIVersion1: false,
configServerCommandName: "_configsvrRepairShardedCollectionChunksHistory",
runsAgainstAdminDb: true,
permittedInTxn: false,
permittedOnShardedCollection: true,
setUp: () => {
assert.commandWorked(st.s.adminCommand({enableSharding: "db", primaryShard: st.shard0.shardName}));
assert.commandWorked(st.s.adminCommand({shardCollection: "db.collection", key: {_id: 1}}));
},
command: () => ({repairShardedCollectionChunksHistory: "db.collection"}),
},
},
{
commandName: "resetPlacementHistory",
// The command is expected to fail when the featureFlagChangeStreamPreciseShardTargeting is disabled.

View File

@ -113,7 +113,6 @@ let testCases = {
_configsvrRemoveShardFromZone: {skip: "internal command"},
_configsvrRemoveTags: {skip: "internal command"},
_configsvrRenameCollection: {skip: "internal command"},
_configsvrRepairShardedCollectionChunksHistory: {skip: "internal command"},
_configsvrResetPlacementHistory: {skip: "internal command"},
_configsvrReshardCollection: {skip: "internal command"},
_configsvrRunRestore: {skip: "internal command"},
@ -712,7 +711,6 @@ let testCases = {
checkReadConcern: false,
checkWriteConcern: true,
},
repairShardedCollectionChunksHistory: {skip: "does not accept read or write concern"},
replicateSearchIndexCommand: {skip: "internal command"},
replSetAbortPrimaryCatchUp: {skip: "does not accept read or write concern"},
replSetFreeze: {skip: "does not accept read or write concern"},

View File

@ -340,7 +340,6 @@ let testCases = {
removeShard: {skip: "primary only"},
removeShardFromZone: {skip: "primary only"},
renameCollection: {skip: "primary only"},
repairShardedCollectionChunksHistory: {skip: "does not return user data"},
replicateSearchIndexCommand: {skip: "internal command for testing only"},
replSetAbortPrimaryCatchUp: {skip: "does not return user data"},
replSetFreeze: {skip: "does not return user data"},

View File

@ -441,7 +441,6 @@ let testCases = {
removeShard: {skip: "primary only"},
removeShardFromZone: {skip: "primary only"},
renameCollection: {skip: "primary only"},
repairShardedCollectionChunksHistory: {skip: "does not return user data"},
replicateSearchIndexCommand: {skip: "internal command"},
replSetAbortPrimaryCatchUp: {skip: "does not return user data"},
replSetFreeze: {skip: "does not return user data"},

View File

@ -356,7 +356,6 @@ let testCases = {
removeShard: {skip: "primary only"},
removeShardFromZone: {skip: "primary only"},
renameCollection: {skip: "primary only"},
repairShardedCollectionChunksHistory: {skip: "does not return user data"},
replicateSearchIndexCommand: {skip: "for testing only"},
replSetAbortPrimaryCatchUp: {skip: "does not return user data"},
replSetFreeze: {skip: "does not return user data"},

View File

@ -416,15 +416,6 @@ idl_generator(
],
)
idl_generator(
name = "repair_sharded_collection_chunks_history_gen",
src = "repair_sharded_collection_chunks_history.idl",
deps = [
"//src/mongo/db:basic_types_gen",
"//src/mongo/idl:generic_argument_gen",
],
)
idl_generator(
name = "upgrade_downgrade_viewless_timeseries_gen",
src = "upgrade_downgrade_viewless_timeseries.idl",

View File

@ -1,131 +0,0 @@
/**
* Copyright (C) 2021-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/base/error_codes.h"
#include "mongo/base/status.h"
#include "mongo/base/string_data.h"
#include "mongo/bson/bsonelement.h"
#include "mongo/bson/bsonmisc.h"
#include "mongo/bson/bsonobj.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/client/read_preference.h"
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/resource_pattern.h"
#include "mongo/db/commands.h"
#include "mongo/db/database_name.h"
#include "mongo/db/global_catalog/ddl/repair_sharded_collection_chunks_history_gen.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/service_context.h"
#include "mongo/db/sharding_environment/client/shard.h"
#include "mongo/db/sharding_environment/grid.h"
#include "mongo/db/topology/shard_registry.h"
#include "mongo/util/assert_util.h"
#include <string>
#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kSharding
namespace mongo {
namespace {
class RepairShardedCollectionChunksHistoryCommand
: public TypedCommand<RepairShardedCollectionChunksHistoryCommand> {
public:
using Request = RepairShardedCollectionChunksHistory;
class Invocation final : public InvocationBase {
public:
using InvocationBase::InvocationBase;
void typedRun(OperationContext* opCtx) {
BSONObjBuilder cmdBuilder;
ConfigsvrRepairShardedCollectionChunksHistory cmd(ns());
cmd.setForce(request().getForce());
cmd.setDbName(DatabaseName::kAdmin);
cmd.serialize(&cmdBuilder);
auto configShard = Grid::get(opCtx)->shardRegistry()->getConfigShard();
auto cmdResponse = uassertStatusOK(
configShard->runCommand(opCtx,
ReadPreferenceSetting{ReadPreference::PrimaryOnly},
DatabaseName::kAdmin,
CommandHelpers::appendMajorityWriteConcern(
cmdBuilder.obj(), opCtx->getWriteConcern()),
Shard::RetryPolicy::kIdempotent));
uassertStatusOK(cmdResponse.commandStatus);
}
private:
NamespaceString ns() const override {
return request().getCommandParameter();
}
bool supportsWriteConcern() const override {
return false;
}
// The command intentionally uses the permission control of split/mergeChunks since it only
// modifies the contents of chunk entries and increments the collection/shard placement
// versions without causing any data placement changes
void doCheckAuthorization(OperationContext* opCtx) const override {
uassert(ErrorCodes::Unauthorized,
"Unauthorized",
AuthorizationSession::get(opCtx->getClient())
->isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns()),
ActionType::splitChunk));
}
};
AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
return AllowedOnSecondary::kAlways;
}
bool adminOnly() const override {
return true;
}
std::string help() const override {
return "Administrative command to repair the effects of SERVER-62065. If the collection "
"has been upgraded through a cluster comprised of binaries which do not contain "
"this command, the chunks cache collections on the shards will miss history "
"entries. This command will correct that and will mark such collections as "
"correctly repaired, so that a subsequent invocation will not cause any changes to "
"the routing information. In rare cases where the history entries are missing due "
"to corrupted restore, the 'force:true' parameter can be passed which will force "
"all history entries to be re-added.";
}
};
MONGO_REGISTER_COMMAND(RepairShardedCollectionChunksHistoryCommand).forRouter();
} // namespace
} // namespace mongo

View File

@ -1,137 +0,0 @@
/**
* Copyright (C) 2021-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/base/error_codes.h"
#include "mongo/base/status.h"
#include "mongo/base/string_data.h"
#include "mongo/bson/bsonelement.h"
#include "mongo/bson/bsonobj.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/authorization_session.h"
#include "mongo/db/auth/resource_pattern.h"
#include "mongo/db/commands.h"
#include "mongo/db/database_name.h"
#include "mongo/db/global_catalog/ddl/repair_sharded_collection_chunks_history_gen.h"
#include "mongo/db/global_catalog/ddl/sharding_catalog_manager.h"
#include "mongo/db/logical_time.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/namespace_string_util.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/repl/read_concern_args.h"
#include "mongo/db/repl/read_concern_level.h"
#include "mongo/db/router_role/routing_cache/catalog_cache.h"
#include "mongo/db/router_role/routing_cache/routing_information_cache.h"
#include "mongo/db/server_options.h"
#include "mongo/db/service_context.h"
#include "mongo/db/topology/cluster_role.h"
#include "mongo/db/topology/vector_clock/vector_clock.h"
#include "mongo/util/assert_util.h"
#include <string>
#include <boost/move/utility_core.hpp>
#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kSharding
namespace mongo {
namespace {
class ConfigSvrRepairShardedCollectionChunksHistoryCommand
: public TypedCommand<ConfigSvrRepairShardedCollectionChunksHistoryCommand> {
public:
using Request = ConfigsvrRepairShardedCollectionChunksHistory;
class Invocation final : public InvocationBase {
public:
using InvocationBase::InvocationBase;
void typedRun(OperationContext* opCtx) {
uassert(
ErrorCodes::IllegalOperation,
"_configsvrRepairShardedCollectionChunksHistory can only be run on config servers",
serverGlobalParams.clusterRole.has(ClusterRole::ConfigServer));
// Set the operation context read concern level to local for reads into the config
// database.
repl::ReadConcernArgs::get(opCtx) =
repl::ReadConcernArgs(repl::ReadConcernLevel::kLocalReadConcern);
CommandHelpers::uassertCommandRunWithMajority(
ConfigsvrRepairShardedCollectionChunksHistory::kCommandName,
opCtx->getWriteConcern());
auto currentTime = VectorClock::get(opCtx)->getTime();
auto validAfter = currentTime.configTime().asTimestamp();
ShardingCatalogManager::get(opCtx)->upgradeChunksHistory(
opCtx, ns(), request().getForce(), validAfter);
RoutingInformationCache::get(opCtx)->invalidateCollectionEntry_LINEARIZABLE(ns());
}
private:
NamespaceString ns() const override {
return request().getCommandParameter();
}
bool supportsWriteConcern() const override {
return true;
}
void doCheckAuthorization(OperationContext* opCtx) const override {
uassert(ErrorCodes::Unauthorized,
"Unauthorized",
AuthorizationSession::get(opCtx->getClient())
->isAuthorizedForActionsOnResource(
ResourcePattern::forClusterResource(request().getDbName().tenantId()),
ActionType::internal));
}
};
AllowedOnSecondary secondaryAllowed(ServiceContext*) const override {
return AllowedOnSecondary::kNever;
}
bool adminOnly() const override {
return true;
}
std::string help() const override {
return "Internal command, which is exported by the sharding config server. Do not call "
"directly.";
}
};
MONGO_REGISTER_COMMAND(ConfigSvrRepairShardedCollectionChunksHistoryCommand).forShard();
} // namespace
} // namespace mongo

View File

@ -1,60 +0,0 @@
# Copyright (C) 2024-present MongoDB, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the Server Side Public License, version 1,
# as published by MongoDB, Inc.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# Server Side Public License for more details.
#
# You should have received a copy of the Server Side Public License
# along with this program. If not, see
# <http://www.mongodb.com/licensing/server-side-public-license>.
#
# As a special exception, the copyright holders give permission to link the
# code of portions of this program with the OpenSSL library under certain
# conditions as described in each individual source file and distribute
# linked combinations including the program with the OpenSSL library. You
# must comply with the Server Side Public License in all respects for
# all of the code used other than as permitted herein. If you modify file(s)
# with this exception, you may extend this exception to your version of the
# file(s), but you are not obligated to do so. If you do not wish to do so,
# delete this exception statement from your version. If you delete this
# exception statement from all source files in the program, then also delete
# it in the license file.
#
global:
mod_visibility: private
cpp_namespace: "mongo"
imports:
- "mongo/db/basic_types.idl"
commands:
repairShardedCollectionChunksHistory:
command_name: repairShardedCollectionChunksHistory
description: "The repairShardedCollectionChunksHistory command on mongos to repair a sharded collection's chunk history due to the effects of SERVER-62065, with an option to forcefully re-add all history entries if needed."
namespace: type
type: namespacestring
api_version: ""
strict: false
fields:
force:
description: "If true, chunks that were repaired earlier by repairShardedCollectionChunksHistory are re-repaired. If false, chunks that were repaired earlier are skipped."
type: optionalBool
_configsvrRepairShardedCollectionChunksHistory:
command_name: _configsvrRepairShardedCollectionChunksHistory
cpp_name: ConfigsvrRepairShardedCollectionChunksHistory
description: "The internal _configsvrRepairShardedCollectionChunksHistory command to repair a sharded collection's chunk history."
namespace: type
type: namespacestring
api_version: ""
strict: false
fields:
force:
description: "If true, chunks that were repaired earlier by repairShardedCollectionChunksHistory are re-repaired. If false, chunks that were repaired earlier are skipped."
type: optionalBool

View File

@ -662,18 +662,6 @@ public:
*/
static void clearForTests(ServiceContext* serviceContext);
//
// Upgrade/downgrade
//
/**
* Upgrade the chunk metadata to include the history field.
*/
void upgradeChunksHistory(OperationContext* opCtx,
const NamespaceString& nss,
bool force,
const Timestamp& validAfter);
/**
* Returns a catalog client that will always run commands locally. Can only be used on a
* config server node.

View File

@ -1813,135 +1813,6 @@ StatusWith<ChunkType> ShardingCatalogManager::_findChunkOnConfig(OperationContex
return ChunkType::parseFromConfigBSON(origChunks.front(), epoch, timestamp);
}
void ShardingCatalogManager::upgradeChunksHistory(OperationContext* opCtx,
const NamespaceString& nss,
bool force,
const Timestamp& validAfter) {
const auto shardRegistry = Grid::get(opCtx)->shardRegistry();
// Mark opCtx as interruptible to ensure that all reads and writes to the metadata collections
// under the exclusive _kChunkOpLock happen on the same term.
opCtx->setAlwaysInterruptAtStepDownOrUp_UNSAFE();
// Take _kChunkOpLock in exclusive mode to prevent concurrent chunk splits, merges, and
// migrations.
Lock::ExclusiveLock lk(opCtx, _kChunkOpLock);
const auto [coll, collPlacementVersion] =
uassertStatusOK(getCollectionAndVersion(opCtx, _localConfigShard.get(), nss));
if (force) {
LOGV2(620650,
"Resetting the 'historyIsAt40' field for all chunks in collection {namespace} in "
"order to force all chunks' history to get recreated",
logAttrs(nss));
BatchedCommandRequest request([collUuid = coll.getUuid()] {
write_ops::UpdateCommandRequest updateOp(NamespaceString::kConfigsvrChunksNamespace);
updateOp.setUpdates({[&] {
write_ops::UpdateOpEntry entry;
entry.setQ(BSON(ChunkType::collectionUUID() << collUuid));
entry.setU(write_ops::UpdateModification::parseFromClassicUpdate(
BSON("$unset" << BSON(ChunkType::historyIsAt40() << ""))));
entry.setUpsert(false);
entry.setMulti(true);
return entry;
}()});
return updateOp;
}());
auto response = _localConfigShard->runBatchWriteCommand(
opCtx,
Milliseconds(defaultConfigCommandTimeoutMS.load()),
request,
ShardingCatalogClient::writeConcernLocalHavingUpstreamWaiter(),
Shard::RetryPolicy::kIdempotent);
uassertStatusOK(response.toStatus());
uassert(ErrorCodes::Error(5760502),
str::stream() << "No chunks found for collection " << nss.toStringForErrorMsg(),
response.getN() > 0);
}
// Find the chunk history
const auto allChunksVector = [&, collUuid = coll.getUuid()] {
auto findChunksResponse = uassertStatusOK(_localConfigShard->exhaustiveFindOnConfig(
opCtx,
ReadPreferenceSetting{ReadPreference::PrimaryOnly},
repl::ReadConcernLevel::kLocalReadConcern,
NamespaceString::kConfigsvrChunksNamespace,
BSON(ChunkType::collectionUUID() << collUuid),
BSONObj(),
boost::none));
uassert(ErrorCodes::Error(5760503),
str::stream() << "No chunks found for collection " << nss.toStringForErrorMsg(),
!findChunksResponse.docs.empty());
return std::move(findChunksResponse.docs);
}();
// Bump the major version in order to be guaranteed to trigger refresh on every shard
ChunkVersion newCollectionPlacementVersion(
{collPlacementVersion.epoch(), collPlacementVersion.getTimestamp()},
{collPlacementVersion.majorVersion() + 1, 0});
std::set<ShardId> shardsOwningChunks;
for (const auto& chunk : allChunksVector) {
auto upgradeChunk = uassertStatusOK(ChunkType::parseFromConfigBSON(
chunk, collPlacementVersion.epoch(), collPlacementVersion.getTimestamp()));
shardsOwningChunks.emplace(upgradeChunk.getShard());
bool historyIsAt40 = chunk[ChunkType::historyIsAt40()].booleanSafe();
if (historyIsAt40) {
uassert(
ErrorCodes::Error(5760504),
str::stream() << "Chunk " << upgradeChunk.getName() << " in collection "
<< nss.toStringForErrorMsg()
<< " indicates that it has been upgraded to version 4.0, but is "
"missing the history field. This indicates a corrupted routing "
"table and requires a manual intervention to be fixed.",
!upgradeChunk.getHistory().empty());
continue;
}
upgradeChunk.setVersion(newCollectionPlacementVersion);
newCollectionPlacementVersion.incMinor();
// Construct the fresh history.
upgradeChunk.setOnCurrentShardSince(validAfter);
upgradeChunk.setHistory(
{ChunkHistory{*upgradeChunk.getOnCurrentShardSince(), upgradeChunk.getShard()}});
// Set the 'historyIsAt40' field so that it gets skipped if the command is re-run
BSONObjBuilder chunkObjBuilder(upgradeChunk.toConfigBSON());
chunkObjBuilder.appendBool(ChunkType::historyIsAt40(), true);
// Run the update
uassertStatusOK(_localCatalogClient->updateConfigDocument(
opCtx,
NamespaceString::kConfigsvrChunksNamespace,
BSON(ChunkType::name(upgradeChunk.getName())),
chunkObjBuilder.obj(),
false,
ShardingCatalogClient::writeConcernLocalHavingUpstreamWaiter()));
}
// Wait for the writes to become majority committed so that the subsequent shard refreshes can
// see them
const auto clientOpTime = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp();
WriteConcernResult unusedWCResult;
uassertStatusOK(waitForWriteConcern(
opCtx, clientOpTime, defaultMajorityWriteConcernDoNotUse(), &unusedWCResult));
for (const auto& shardId : shardsOwningChunks) {
auto shard = uassertStatusOK(shardRegistry->getShard(opCtx, shardId));
uassertStatusOK(Shard::CommandResponse::getEffectiveStatus(shard->runCommand(
opCtx,
ReadPreferenceSetting{ReadPreference::PrimaryOnly},
DatabaseName::kAdmin,
BSON("_flushRoutingTableCacheUpdates"
<< NamespaceStringUtil::serialize(nss, SerializationContext::stateDefault())),
Shard::RetryPolicy::kIdempotent)));
}
}
void ShardingCatalogManager::clearJumboFlag(OperationContext* opCtx,
const NamespaceString& nss,
const OID& collectionEpoch,

View File

@ -582,7 +582,6 @@ mongo_cc_library(
"//src/mongo/db/global_catalog/ddl:configsvr_merge_chunks_command.cpp",
"//src/mongo/db/global_catalog/ddl:configsvr_remove_chunks_command.cpp",
"//src/mongo/db/global_catalog/ddl:configsvr_remove_shard_from_zone_command.cpp",
"//src/mongo/db/global_catalog/ddl:configsvr_repair_sharded_collection_chunks_history_command.cpp",
"//src/mongo/db/global_catalog/ddl:configsvr_reset_placement_history_command.cpp",
"//src/mongo/db/global_catalog/ddl:configsvr_run_restore_command.cpp",
"//src/mongo/db/global_catalog/ddl:configsvr_run_restore_gen",

View File

@ -289,7 +289,6 @@ mongo_cc_library(
"//src/mongo/db/global_catalog/ddl:move_primary_gen",
"//src/mongo/db/global_catalog/ddl:placement_history_commands_gen",
"//src/mongo/db/global_catalog/ddl:remove_shard_from_zone_request_type.cpp",
"//src/mongo/db/global_catalog/ddl:repair_sharded_collection_chunks_history_gen",
"//src/mongo/db/global_catalog/ddl:set_allow_migrations_gen",
"//src/mongo/db/global_catalog/ddl:sharded_ddl_commands_gen",
"//src/mongo/db/global_catalog/ddl:shardsvr_join_ddl_coordinators_request_gen",

View File

@ -126,7 +126,6 @@ mongo_cc_library(
"//src/mongo/db/global_catalog/ddl:cluster_recreate_range_deletion_tasks_command.cpp",
"//src/mongo/db/global_catalog/ddl:cluster_refine_collection_shard_key_cmd.cpp",
"//src/mongo/db/global_catalog/ddl:cluster_rename_collection_cmd.cpp",
"//src/mongo/db/global_catalog/ddl:cluster_repair_sharded_collection_chunks_history_cmd.cpp",
"//src/mongo/db/global_catalog/ddl:cluster_reset_placement_history_cmd.cpp",
"//src/mongo/db/global_catalog/ddl:cluster_set_allow_migrations_cmd.cpp",
"//src/mongo/db/global_catalog/ddl:cluster_shard_collection_cmd.cpp",