0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00

SERVER-41825 Remove FCV checks related to mutable shard key fields

This commit is contained in:
jannaerin 2019-06-26 17:11:20 -04:00
parent 644fc54e18
commit d960519275
2 changed files with 5 additions and 291 deletions

View File

@ -1,273 +0,0 @@
// Test that shard key fields cannot be updated when one or more shards is in FCV 4.0.
// @tags: [uses_transactions, uses_multi_shard_transaction]
(function() {
"use strict";
load("jstests/libs/feature_compatibility_version.js");
load("jstests/sharding/libs/update_shard_key_helpers.js");
let st = new ShardingTest({
shards: {rs0: {nodes: 3, binVersion: "latest"}, rs1: {nodes: 3, binVersion: "latest"}},
mongos: 1,
other: {mongosOptions: {binVersion: "latest"}, configOptions: {binVersion: "latest"}}
});
let mongos = st.s0;
let kDbName = "test";
let collName = "foo";
let ns = kDbName + "." + collName;
assert.commandWorked(mongos.adminCommand({enableSharding: kDbName}));
st.ensurePrimaryShard(kDbName, st.shard0.shardName);
function shardCollectionAndMoveChunks(docsToInsert, shardKey, splitDoc, moveDoc) {
for (let i = 0; i < docsToInsert.length; i++) {
assert.commandWorked(mongos.getDB(kDbName).foo.insert(docsToInsert[i]));
}
assert.commandWorked(mongos.getDB(kDbName).foo.createIndex(shardKey));
assert.commandWorked(mongos.adminCommand({shardCollection: ns, key: shardKey}));
assert.commandWorked(mongos.adminCommand({split: ns, find: splitDoc}));
assert.commandWorked(
mongos.adminCommand({moveChunk: ns, find: moveDoc, to: st.shard1.shardName}));
assert.commandWorked(mongos.adminCommand({flushRouterConfig: 1}));
st.rs0.getPrimary().adminCommand({_flushRoutingTableCacheUpdates: ns});
st.rs1.getPrimary().adminCommand({_flushRoutingTableCacheUpdates: ns});
st.rs0.getPrimary().adminCommand({_flushDatabaseCacheUpdates: kDbName});
st.rs1.getPrimary().adminCommand({_flushDatabaseCacheUpdates: kDbName});
}
function assertCannotUpdateShardKey(isMixedCluster) {
// ------------------------------------------------
// Test changes to shard key run as retryable write
// ------------------------------------------------
let session = st.s.startSession({retryWrites: true});
let sessionDB = session.getDatabase(kDbName);
// Updates to full shard key
shardCollectionMoveChunks(
st, kDbName, ns, {x: 1}, [{x: 30}, {x: 50}, {x: 80}], {x: 50}, {x: 80});
cleanupOrphanedDocs(st, ns);
// Assert that updating the shard key when the doc would remain on the same shard fails for
// both modify and replacement updates
assert.writeError(sessionDB.foo.update({x: 80}, {$set: {x: 100}}));
assert.writeError(sessionDB.foo.update({x: 80}, {x: 100}));
assert.throws(function() {
sessionDB.foo.findAndModify({query: {x: 80}, update: {$set: {x: 100}}});
});
assert.throws(function() {
sessionDB.foo.findAndModify({query: {x: 80}, update: {x: 100}});
});
// Assert that updating the shard key when the doc would move shards fails for both modify
// and replacement updates.
assert.writeError(sessionDB.foo.update({x: 80}, {$set: {x: 3}}));
assert.commandFailedWithCode(sessionDB.foo.update({x: 80}, {x: 3}),
[ErrorCodes.ImmutableField, ErrorCodes.InvalidOptions]);
assert.eq(1, mongos.getDB(kDbName).foo.find({x: 80}).itcount());
assert.eq(0, mongos.getDB(kDbName).foo.find({x: 3}).itcount());
assert.throws(function() {
sessionDB.foo.findAndModify({query: {x: 80}, update: {$set: {x: 3}}});
});
assert.throws(function() {
sessionDB.foo.findAndModify({query: {x: 80}, update: {x: 3}});
});
mongos.getDB(kDbName).foo.drop();
// Updates to partial shard key
shardCollectionMoveChunks(st,
kDbName,
ns,
{x: 1, y: 1},
[{x: 30, y: 4}, {x: 50, y: 50}, {x: 80, y: 100}],
{x: 50, y: 50},
{x: 80, y: 100});
cleanupOrphanedDocs(st, ns);
// Assert that updating the shard key when the doc would remain on the same shard fails for
// both modify and replacement updates
assert.writeError(sessionDB.foo.update({x: 80}, {$set: {x: 100}}));
assert.writeError(sessionDB.foo.update({x: 80}, {x: 100}));
assert.throws(function() {
sessionDB.foo.findAndModify({query: {x: 80}, update: {$set: {x: 100}}});
});
assert.throws(function() {
sessionDB.foo.findAndModify({query: {x: 80}, update: {x: 100}});
});
// Assert that updating the shard key when the doc would move shards fails for both modify
// and replacement updates
assert.writeError(sessionDB.foo.update({x: 80}, {$set: {x: 3}}));
assert.writeError(sessionDB.foo.update({x: 80}, {x: 3}));
assert.throws(function() {
sessionDB.foo.findAndModify({query: {x: 80}, update: {$set: {x: 3}}});
});
assert.throws(function() {
sessionDB.foo.findAndModify({query: {x: 80}, update: {x: 3}});
});
mongos.getDB(kDbName).foo.drop();
// -----------------------------------------------
// Test changes to shard key run in a transaction
// -----------------------------------------------
session = st.s.startSession();
sessionDB = session.getDatabase(kDbName);
// Updates to full shard key
shardCollectionMoveChunks(
st, kDbName, ns, {x: 1}, [{x: 30}, {x: 50}, {x: 80}], {x: 50}, {x: 80});
cleanupOrphanedDocs(st, ns);
// Assert that updating the shard key when the doc would remain on the same shard fails for
// both modify and replacement updates
session.startTransaction();
assert.writeError(sessionDB.foo.update({x: 80}, {$set: {x: 100}}));
assert.commandFailedWithCode(session.abortTransaction_forTesting(),
ErrorCodes.NoSuchTransaction);
session.startTransaction();
assert.throws(function() {
sessionDB.foo.findAndModify({query: {x: 80}, update: {x: 100}});
});
assert.commandFailedWithCode(session.abortTransaction_forTesting(),
ErrorCodes.NoSuchTransaction);
mongos.getDB(kDbName).foo.drop();
if (isMixedCluster) {
// Assert that updating the shard key when the doc would move shards fails on commit
// because one of the participants is not in FCV 4.2. If the original write is a
// retryable write, the write will fail when mongos attempts to run commitTransaction.
// If the original write is part of a transaction, the write itself will complete
// successfully, but the transaction will fail to commit.
// Retryable write - updates to full shard key
session = st.s.startSession({retryWrites: true});
sessionDB = session.getDatabase(kDbName);
shardCollectionMoveChunks(
st, kDbName, ns, {x: 1}, [{x: 30}, {x: 50}, {x: 80}], {x: 50}, {x: 80});
cleanupOrphanedDocs(st, ns);
// Doc will move shards
assert.writeError(sessionDB.foo.update({x: 30}, {$set: {x: 100}}));
assert.throws(function() {
sessionDB.foo.findAndModify({query: {x: 30}, update: {$set: {x: 100}}});
});
// Doc will remain on the same shard. Because shard 0 is on FCV 4.2, these should
// complete successfully.
sessionDB.foo.findAndModify({query: {x: 30}, update: {$set: {x: 5}}});
st.rs0.awaitReplication();
assert.eq(1, st.rs0.getPrimary().getDB(kDbName).foo.find({x: 5}).itcount());
assert.eq(1, st.rs0.getSecondaries()[0].getDB(kDbName).foo.find({x: 5}).itcount());
assert.eq(0, st.rs0.getPrimary().getDB(kDbName).foo.find({x: 30}).itcount());
assert.eq(0, st.rs0.getSecondaries()[0].getDB(kDbName).foo.find({x: 30}).itcount());
sessionDB.foo.findAndModify({query: {x: 5}, update: {x: 10}});
st.rs0.awaitReplication();
assert.eq(0, st.rs0.getPrimary().getDB(kDbName).foo.find({x: 5}).itcount());
assert.eq(0, st.rs0.getSecondaries()[0].getDB(kDbName).foo.find({x: 5}).itcount());
assert.eq(1, st.rs0.getPrimary().getDB(kDbName).foo.find({x: 10}).itcount());
assert.eq(1, st.rs0.getSecondaries()[0].getDB(kDbName).foo.find({x: 10}).itcount());
assert.commandWorked(sessionDB.foo.update({x: 10}, {$set: {x: 25}}));
st.rs0.awaitReplication();
assert.eq(1, st.rs0.getPrimary().getDB(kDbName).foo.find({x: 25}).itcount());
assert.eq(1, st.rs0.getSecondaries()[0].getDB(kDbName).foo.find({x: 25}).itcount());
assert.eq(0, st.rs0.getPrimary().getDB(kDbName).foo.find({x: 10}).itcount());
assert.eq(0, st.rs0.getSecondaries()[0].getDB(kDbName).foo.find({x: 10}).itcount());
assert.commandWorked(sessionDB.foo.update({x: 25}, {x: 3}));
st.rs0.awaitReplication();
assert.eq(0, st.rs0.getPrimary().getDB(kDbName).foo.find({x: 25}).itcount());
assert.eq(0, st.rs0.getSecondaries()[0].getDB(kDbName).foo.find({x: 25}).itcount());
assert.eq(1, st.rs0.getPrimary().getDB(kDbName).foo.find({x: 3}).itcount());
assert.eq(1, st.rs0.getSecondaries()[0].getDB(kDbName).foo.find({x: 3}).itcount());
mongos.getDB(kDbName).foo.drop();
// Retryable write - updates to partial shard key
shardCollectionMoveChunks(st,
kDbName,
ns,
{x: 1, y: 1},
[{x: 30, y: 4}, {x: 50, y: 50}, {x: 80, y: 100}],
{x: 50, y: 50},
{x: 80, y: 100});
cleanupOrphanedDocs(st, ns);
assert.writeError(sessionDB.foo.update({x: 30}, {$set: {x: 100}}));
assert.throws(function() {
sessionDB.foo.findAndModify({query: {x: 30}, update: {x: 100}});
});
mongos.getDB(kDbName).foo.drop();
// Transactional writes
session = st.s.startSession();
sessionDB = session.getDatabase(kDbName);
shardCollectionMoveChunks(
st, kDbName, ns, {x: 1}, [{x: 10}, {x: 30}, {x: 50}, {x: 80}], {x: 50}, {x: 80});
cleanupOrphanedDocs(st, ns);
session.startTransaction();
assert.commandWorked(sessionDB.foo.update({x: 30}, {$set: {x: 100}}));
assert.commandFailed(session.commitTransaction_forTesting());
assert.eq(1, mongos.getDB(kDbName).foo.find({x: 30}).itcount());
assert.eq(0, mongos.getDB(kDbName).foo.find({x: 100}).itcount());
session.startTransaction();
sessionDB.foo.findAndModify({query: {x: 30}, update: {x: 100}});
assert.commandFailed(session.commitTransaction_forTesting());
assert.eq(1, mongos.getDB(kDbName).foo.find({x: 30}).itcount());
assert.eq(0, mongos.getDB(kDbName).foo.find({x: 100}).itcount());
// Doc will remain on the same shard. Because shard 0 is on FCV 4.2, this should
// complete successfully.
session.startTransaction();
sessionDB.foo.findAndModify({query: {x: 10}, update: {x: 1}});
assert.commandWorked(sessionDB.foo.update({x: 30}, {$set: {x: 5}}));
assert.commandWorked(session.commitTransaction_forTesting());
st.rs0.awaitReplication();
assert.eq(0, st.rs0.getPrimary().getDB(kDbName).foo.find({x: 30}).itcount());
assert.eq(0, st.rs0.getSecondaries()[0].getDB(kDbName).foo.find({x: 30}).itcount());
assert.eq(0, st.rs0.getPrimary().getDB(kDbName).foo.find({x: 10}).itcount());
assert.eq(0, st.rs0.getSecondaries()[0].getDB(kDbName).foo.find({x: 10}).itcount());
assert.eq(1, st.rs0.getPrimary().getDB(kDbName).foo.find({x: 5}).itcount());
assert.eq(1, st.rs0.getSecondaries()[0].getDB(kDbName).foo.find({x: 5}).itcount());
assert.eq(1, st.rs0.getPrimary().getDB(kDbName).foo.find({x: 1}).itcount());
assert.eq(1, st.rs0.getSecondaries()[0].getDB(kDbName).foo.find({x: 1}).itcount());
mongos.getDB(kDbName).foo.drop();
}
}
// Check that updating the shard key fails when all shards are in FCV 4.0
assert.commandWorked(st.s.getDB("admin").runCommand({setFeatureCompatibilityVersion: "4.0"}));
checkFCV(st.configRS.getPrimary().getDB("admin"), "4.0");
checkFCV(st.rs0.getPrimary().getDB("admin"), "4.0");
checkFCV(st.rs1.getPrimary().getDB("admin"), "4.0");
assertCannotUpdateShardKey(false);
// Check that updating the shard key fails when shard0 is in FCV 4.2 but shard 1 is in FCV 4.0
assert.commandWorked(
st.rs0.getPrimary().getDB("admin").runCommand({setFeatureCompatibilityVersion: "4.2"}));
checkFCV(st.configRS.getPrimary().getDB("admin"), "4.0");
checkFCV(st.rs0.getPrimary().getDB("admin"), "4.2");
checkFCV(st.rs1.getPrimary().getDB("admin"), "4.0");
assertCannotUpdateShardKey(true);
st.stop();
})();

View File

@ -213,10 +213,6 @@ BSONObj UpdateStage::transformAndUpdate(const Snapshotted<BSONObj>& oldObj, Reco
bool docWasModified = false;
const auto isFCV42 = serverGlobalParams.featureCompatibility.isVersionInitialized() &&
serverGlobalParams.featureCompatibility.getVersion() ==
ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo42;
auto* const css = CollectionShardingState::get(getOpCtx(), collection()->ns());
auto metadata = css->getCurrentMetadata();
Status status = Status::OK();
@ -224,8 +220,7 @@ BSONObj UpdateStage::transformAndUpdate(const Snapshotted<BSONObj>& oldObj, Reco
const bool isInsert = false;
FieldRefSet immutablePaths;
if (getOpCtx()->writesAreReplicated() && !request->isFromMigration()) {
if (metadata->isSharded() &&
(!OperationShardingState::isOperationVersioned(getOpCtx()) || !isFCV42)) {
if (metadata->isSharded() && !OperationShardingState::isOperationVersioned(getOpCtx())) {
auto& immutablePathsVector = metadata->getKeyPatternFields();
immutablePaths.fillFrom(
transitional_tools_do_not_use::unspool_vector(immutablePathsVector));
@ -323,7 +318,7 @@ BSONObj UpdateStage::transformAndUpdate(const Snapshotted<BSONObj>& oldObj, Reco
Snapshotted<RecordData> snap(oldObj.snapshotId(), oldRec);
if (isFCV42 && metadata->isSharded() && _shouldCheckForShardKeyUpdate) {
if (metadata->isSharded() && _shouldCheckForShardKeyUpdate) {
bool changesShardKeyOnSameNode =
checkUpdateChangesShardKeyFields(metadata, oldObj);
if (changesShardKeyOnSameNode && !args.preImageDoc) {
@ -351,7 +346,7 @@ BSONObj UpdateStage::transformAndUpdate(const Snapshotted<BSONObj>& oldObj, Reco
newObj.objsize() <= BSONObjMaxUserSize);
if (!request->isExplain()) {
if (isFCV42 && metadata->isSharded() && _shouldCheckForShardKeyUpdate) {
if (metadata->isSharded() && _shouldCheckForShardKeyUpdate) {
bool changesShardKeyOnSameNode =
checkUpdateChangesShardKeyFields(metadata, oldObj);
if (changesShardKeyOnSameNode && !args.preImageDoc) {
@ -412,13 +407,8 @@ BSONObj UpdateStage::applyUpdateOpsForInsert(OperationContext* opCtx,
auto* const css = CollectionShardingState::get(opCtx, ns);
auto metadata = css->getCurrentMetadata();
const auto isFCV42 = serverGlobalParams.featureCompatibility.isVersionInitialized() &&
serverGlobalParams.featureCompatibility.getVersion() ==
ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo42;
FieldRefSet immutablePaths;
if (metadata->isSharded() &&
(!OperationShardingState::isOperationVersioned(opCtx) || !isFCV42)) {
if (metadata->isSharded() && !OperationShardingState::isOperationVersioned(opCtx)) {
auto& immutablePathsVector = metadata->getKeyPatternFields();
immutablePaths.fillFrom(
transitional_tools_do_not_use::unspool_vector(immutablePathsVector));
@ -594,13 +584,10 @@ void UpdateStage::doInsert() {
// fields in the 'q' field belong to this shard, but those in the 'u' field do not. In this case
// we need to throw so that MongoS can target the insert to the correct shard.
if (_shouldCheckForShardKeyUpdate) {
const auto isFCV42 = serverGlobalParams.featureCompatibility.isVersionInitialized() &&
serverGlobalParams.featureCompatibility.getVersion() ==
ServerGlobalParams::FeatureCompatibility::Version::kFullyUpgradedTo42;
auto* const css = CollectionShardingState::get(getOpCtx(), collection()->ns());
const auto& metadata = css->getCurrentMetadata();
if (isFCV42 && metadata->isSharded()) {
if (metadata->isSharded()) {
const ShardKeyPattern shardKeyPattern(metadata->getKeyPattern());
auto newShardKey = shardKeyPattern.extractShardKeyFromDoc(newObj);