summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/exec/update_stage.cpp22
-rw-r--r--src/mongo/db/exec/update_stage.h7
-rw-r--r--src/mongo/db/op_observer_impl.cpp1
-rw-r--r--src/mongo/db/op_observer_impl.h3
-rw-r--r--src/mongo/db/s/migration_chunk_cloner_source.h3
-rw-r--r--src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp17
-rw-r--r--src/mongo/db/s/migration_chunk_cloner_source_legacy.h3
-rw-r--r--src/mongo/db/s/op_observer_sharding_impl.cpp10
-rw-r--r--src/mongo/db/s/op_observer_sharding_impl.h1
9 files changed, 49 insertions, 18 deletions
diff --git a/src/mongo/db/exec/update_stage.cpp b/src/mongo/db/exec/update_stage.cpp
index 9a309849206..d64b39bd2c3 100644
--- a/src/mongo/db/exec/update_stage.cpp
+++ b/src/mongo/db/exec/update_stage.cpp
@@ -315,7 +315,11 @@ BSONObj UpdateStage::transformAndUpdate(const Snapshotted<BSONObj>& oldObj, Reco
Snapshotted<RecordData> snap(oldObj.snapshotId(), oldRec);
if (isFCV42 && metadata->isSharded()) {
- assertUpdateToShardKeyFieldsIsValidAndDocStillBelongsToNode(metadata, oldObj);
+ bool changesShardKeyOnSameNode =
+ checkUpdateChangesShardKeyFields(metadata, oldObj);
+ if (changesShardKeyOnSameNode && !args.preImageDoc) {
+ args.preImageDoc = oldObj.value().getOwned();
+ }
}
WriteUnitOfWork wunit(getOpCtx());
@@ -339,7 +343,11 @@ BSONObj UpdateStage::transformAndUpdate(const Snapshotted<BSONObj>& oldObj, Reco
if (!request->isExplain()) {
if (isFCV42 && metadata->isSharded()) {
- assertUpdateToShardKeyFieldsIsValidAndDocStillBelongsToNode(metadata, oldObj);
+ bool changesShardKeyOnSameNode =
+ checkUpdateChangesShardKeyFields(metadata, oldObj);
+ if (changesShardKeyOnSameNode && !args.preImageDoc) {
+ args.preImageDoc = oldObj.value().getOwned();
+ }
}
WriteUnitOfWork wunit(getOpCtx());
@@ -888,8 +896,8 @@ PlanStage::StageState UpdateStage::prepareToRetryWSM(WorkingSetID idToRetry, Wor
return NEED_YIELD;
}
-void UpdateStage::assertUpdateToShardKeyFieldsIsValidAndDocStillBelongsToNode(
- ScopedCollectionMetadata metadata, const Snapshotted<BSONObj>& oldObj) {
+bool UpdateStage::checkUpdateChangesShardKeyFields(ScopedCollectionMetadata metadata,
+ const Snapshotted<BSONObj>& oldObj) {
auto newObj = _doc.getObject();
auto oldShardKey = metadata->extractDocumentKey(oldObj.value());
auto newShardKey = metadata->extractDocumentKey(newObj);
@@ -897,7 +905,7 @@ void UpdateStage::assertUpdateToShardKeyFieldsIsValidAndDocStillBelongsToNode(
// If the shard key fields remain unchanged by this update or if this document is an orphan and
// so does not belong to this shard, we can skip the rest of the checks.
if ((newShardKey.woCompare(oldShardKey) == 0) || !metadata->keyBelongsToMe(oldShardKey)) {
- return;
+ return false;
}
// Assert that the updated doc has all shard key fields and none are arrays or array
@@ -930,6 +938,10 @@ void UpdateStage::assertUpdateToShardKeyFieldsIsValidAndDocStillBelongsToNode(
uasserted(WouldChangeOwningShardInfo(oldObj.value(), newObj),
str::stream() << "This update would cause the doc to change owning shards");
}
+
+ // We passed all checks, so we will return that this update changes the shard key field, and
+ // the updated document will remain on the same node.
+ return true;
}
} // namespace mongo
diff --git a/src/mongo/db/exec/update_stage.h b/src/mongo/db/exec/update_stage.h
index 457e9e550d0..a1222499bb2 100644
--- a/src/mongo/db/exec/update_stage.h
+++ b/src/mongo/db/exec/update_stage.h
@@ -205,9 +205,12 @@ private:
* doc no longer belongs to this shard, this means that one or more shard key field values have
* been updated to a value belonging to a chunk that is not owned by this shard. We cannot apply
* this update atomically.
+ *
+ * If the update changes shard key fields but the new shard key remains on the same node,
+ * returns true. If the update does not change shard key fields, returns false.
*/
- void assertUpdateToShardKeyFieldsIsValidAndDocStillBelongsToNode(
- ScopedCollectionMetadata metadata, const Snapshotted<BSONObj>& oldObj);
+ bool checkUpdateChangesShardKeyFields(ScopedCollectionMetadata metadata,
+ const Snapshotted<BSONObj>& oldObj);
UpdateStageParams _params;
diff --git a/src/mongo/db/op_observer_impl.cpp b/src/mongo/db/op_observer_impl.cpp
index f17825ace28..727b7156792 100644
--- a/src/mongo/db/op_observer_impl.cpp
+++ b/src/mongo/db/op_observer_impl.cpp
@@ -605,6 +605,7 @@ void OpObserverImpl::onUpdate(OperationContext* opCtx, const OplogUpdateEntryArg
if (!args.updateArgs.fromMigrate) {
shardObserveUpdateOp(opCtx,
args.nss,
+ args.updateArgs.preImageDoc,
args.updateArgs.updatedDoc,
opTime.writeOpTime,
opTime.prePostImageOpTime,
diff --git a/src/mongo/db/op_observer_impl.h b/src/mongo/db/op_observer_impl.h
index 29b319d77c7..07c7542c342 100644
--- a/src/mongo/db/op_observer_impl.h
+++ b/src/mongo/db/op_observer_impl.h
@@ -169,7 +169,8 @@ private:
const bool inMultiDocumentTransaction) {}
virtual void shardObserveUpdateOp(OperationContext* opCtx,
const NamespaceString nss,
- const BSONObj& updatedDoc,
+ boost::optional<BSONObj> preImageDoc,
+ const BSONObj& postImageDoc,
const repl::OpTime& opTime,
const repl::OpTime& prePostImageOpTime,
const bool inMultiDocumentTransaction) {}
diff --git a/src/mongo/db/s/migration_chunk_cloner_source.h b/src/mongo/db/s/migration_chunk_cloner_source.h
index 6a734595ba4..c871a3e08a8 100644
--- a/src/mongo/db/s/migration_chunk_cloner_source.h
+++ b/src/mongo/db/s/migration_chunk_cloner_source.h
@@ -139,7 +139,8 @@ public:
* NOTE: Must be called with at least IX lock held on the collection.
*/
virtual void onUpdateOp(OperationContext* opCtx,
- const BSONObj& updatedDoc,
+ boost::optional<BSONObj> preImageDoc,
+ const BSONObj& postImageDoc,
const repl::OpTime& opTime,
const repl::OpTime& prePostImageOpTime) = 0;
diff --git a/src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp b/src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp
index 3ad2f76461c..5a5fc344b73 100644
--- a/src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp
+++ b/src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp
@@ -377,19 +377,28 @@ void MigrationChunkClonerSourceLegacy::onInsertOp(OperationContext* opCtx,
}
void MigrationChunkClonerSourceLegacy::onUpdateOp(OperationContext* opCtx,
- const BSONObj& updatedDoc,
+ boost::optional<BSONObj> preImageDoc,
+ const BSONObj& postImageDoc,
const repl::OpTime& opTime,
const repl::OpTime& prePostImageOpTime) {
dassert(opCtx->lockState()->isCollectionLockedForMode(_args.getNss(), MODE_IX));
- BSONElement idElement = updatedDoc["_id"];
+ BSONElement idElement = postImageDoc["_id"];
if (idElement.eoo()) {
warning() << "logUpdateOp got a document with no _id field, ignoring updatedDoc: "
- << redact(updatedDoc);
+ << redact(postImageDoc);
return;
}
- if (!isInRange(updatedDoc, _args.getMinKey(), _args.getMaxKey(), _shardKeyPattern)) {
+ if (!isInRange(postImageDoc, _args.getMinKey(), _args.getMaxKey(), _shardKeyPattern)) {
+ // If the preImageDoc is not in range but the postImageDoc was, we know that the document
+ // has changed shard keys and no longer belongs in the chunk being cloned. We will model
+ // the deletion of the preImage document so that the destination chunk does not receive an
+ // outdated version of this document.
+ if (preImageDoc &&
+ isInRange(*preImageDoc, _args.getMinKey(), _args.getMaxKey(), _shardKeyPattern)) {
+ onDeleteOp(opCtx, *preImageDoc, opTime, prePostImageOpTime);
+ }
return;
}
diff --git a/src/mongo/db/s/migration_chunk_cloner_source_legacy.h b/src/mongo/db/s/migration_chunk_cloner_source_legacy.h
index 1fd34944a8b..42b86cf696e 100644
--- a/src/mongo/db/s/migration_chunk_cloner_source_legacy.h
+++ b/src/mongo/db/s/migration_chunk_cloner_source_legacy.h
@@ -81,7 +81,8 @@ public:
const repl::OpTime& opTime) override;
void onUpdateOp(OperationContext* opCtx,
- const BSONObj& updatedDoc,
+ boost::optional<BSONObj> preImageDoc,
+ const BSONObj& postImageDoc,
const repl::OpTime& opTime,
const repl::OpTime& prePostImageOpTime) override;
diff --git a/src/mongo/db/s/op_observer_sharding_impl.cpp b/src/mongo/db/s/op_observer_sharding_impl.cpp
index d4eff6b15d9..27197a85f5c 100644
--- a/src/mongo/db/s/op_observer_sharding_impl.cpp
+++ b/src/mongo/db/s/op_observer_sharding_impl.cpp
@@ -119,7 +119,8 @@ void OpObserverShardingImpl::shardObserveInsertOp(OperationContext* opCtx,
void OpObserverShardingImpl::shardObserveUpdateOp(OperationContext* opCtx,
const NamespaceString nss,
- const BSONObj& updatedDoc,
+ boost::optional<BSONObj> preImageDoc,
+ const BSONObj& postImageDoc,
const repl::OpTime& opTime,
const repl::OpTime& prePostImageOpTime,
const bool inMultiDocumentTransaction) {
@@ -127,14 +128,14 @@ void OpObserverShardingImpl::shardObserveUpdateOp(OperationContext* opCtx,
csr->checkShardVersionOrThrow(opCtx);
if (inMultiDocumentTransaction) {
- assertIntersectingChunkHasNotMoved(opCtx, csr, updatedDoc);
+ assertIntersectingChunkHasNotMoved(opCtx, csr, postImageDoc);
return;
}
auto csrLock = CollectionShardingRuntime::CSRLock::lock(opCtx, csr);
auto msm = MigrationSourceManager::get(csr, csrLock);
if (msm) {
- msm->getCloner()->onUpdateOp(opCtx, updatedDoc, opTime, prePostImageOpTime);
+ msm->getCloner()->onUpdateOp(opCtx, preImageDoc, postImageDoc, opTime, prePostImageOpTime);
}
}
@@ -182,7 +183,8 @@ void OpObserverShardingImpl::shardObserveTransactionPrepareOrUnpreparedCommit(
msm->getCloner()->onInsertOp(opCtx, stmt.getObject(), {});
} else if (opType == repl::OpTypeEnum::kUpdate) {
if (auto updateDoc = stmt.getObject2()) {
- msm->getCloner()->onUpdateOp(opCtx, *updateDoc, {}, {});
+ msm->getCloner()->onUpdateOp(
+ opCtx, stmt.getPreImageDocumentKey(), *updateDoc, {}, {});
}
} else if (opType == repl::OpTypeEnum::kDelete) {
if (isMigratingWithCSRLock(csr, csrLock, stmt.getObject())) {
diff --git a/src/mongo/db/s/op_observer_sharding_impl.h b/src/mongo/db/s/op_observer_sharding_impl.h
index 5232c8f92d9..4a8baa1ff9f 100644
--- a/src/mongo/db/s/op_observer_sharding_impl.h
+++ b/src/mongo/db/s/op_observer_sharding_impl.h
@@ -54,6 +54,7 @@ protected:
const bool inMultiDocumentTransaction) override;
void shardObserveUpdateOp(OperationContext* opCtx,
const NamespaceString nss,
+ boost::optional<BSONObj> preImageDoc,
const BSONObj& updatedDoc,
const repl::OpTime& opTime,
const repl::OpTime& prePostImageOpTime,