diff options
-rw-r--r-- | src/mongo/db/exec/update_stage.cpp | 22 | ||||
-rw-r--r-- | src/mongo/db/exec/update_stage.h | 7 | ||||
-rw-r--r-- | src/mongo/db/op_observer_impl.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/op_observer_impl.h | 3 | ||||
-rw-r--r-- | src/mongo/db/s/migration_chunk_cloner_source.h | 3 | ||||
-rw-r--r-- | src/mongo/db/s/migration_chunk_cloner_source_legacy.cpp | 17 | ||||
-rw-r--r-- | src/mongo/db/s/migration_chunk_cloner_source_legacy.h | 3 | ||||
-rw-r--r-- | src/mongo/db/s/op_observer_sharding_impl.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/s/op_observer_sharding_impl.h | 1 |
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, |