diff options
author | Dan Larkin-York <dan.larkin-york@mongodb.com> | 2021-04-28 21:04:54 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-04-28 22:07:09 +0000 |
commit | 09e418805e9019cb56e54d240c7c7d9bcb95e339 (patch) | |
tree | 9c0f988d9b9bdfa0b808b84849d1c8b87497bcc6 /src/mongo/db | |
parent | 6e88994a34e7882e1c9692fea653ed625e6e1c1b (diff) | |
download | mongo-09e418805e9019cb56e54d240c7c7d9bcb95e339.tar.gz |
SERVER-55501 Avoid element-wise iteration and copy when appending to an object in doc_diff::applyDiff
Diffstat (limited to 'src/mongo/db')
-rw-r--r-- | src/mongo/db/commands/oplog_application_checks.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/commands/write_commands.cpp | 20 | ||||
-rw-r--r-- | src/mongo/db/ops/write_ops.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/ops/write_ops_exec.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/ops/write_ops_parsers.h | 34 | ||||
-rw-r--r-- | src/mongo/db/repl/oplog.cpp | 12 | ||||
-rw-r--r-- | src/mongo/db/s/resharding/resharding_oplog_application.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/update/delta_executor.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/update/delta_executor.h | 7 | ||||
-rw-r--r-- | src/mongo/db/update/delta_executor_test.cpp | 76 | ||||
-rw-r--r-- | src/mongo/db/update/document_diff_applier.cpp | 31 | ||||
-rw-r--r-- | src/mongo/db/update/document_diff_applier.h | 11 | ||||
-rw-r--r-- | src/mongo/db/update/document_diff_test.cpp | 101 | ||||
-rw-r--r-- | src/mongo/db/update/document_diff_test_helpers.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/update/document_diff_test_helpers.h | 4 | ||||
-rw-r--r-- | src/mongo/db/update/update_driver.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/update/update_driver_test.cpp | 9 |
17 files changed, 253 insertions, 89 deletions
diff --git a/src/mongo/db/commands/oplog_application_checks.cpp b/src/mongo/db/commands/oplog_application_checks.cpp index 88022111696..bd6198ea3a5 100644 --- a/src/mongo/db/commands/oplog_application_checks.cpp +++ b/src/mongo/db/commands/oplog_application_checks.cpp @@ -124,7 +124,8 @@ Status OplogApplicationChecks::checkOperationAuthorization(OperationContext* opC opCtx, ns, o2, - write_ops::UpdateModification::parseFromOplogEntry(o), + write_ops::UpdateModification::parseFromOplogEntry( + o, write_ops::UpdateModification::DiffOptions{}), upsert); } else if (opType == "d"_sd) { diff --git a/src/mongo/db/commands/write_commands.cpp b/src/mongo/db/commands/write_commands.cpp index 096f12b75e6..08abd5cd743 100644 --- a/src/mongo/db/commands/write_commands.cpp +++ b/src/mongo/db/commands/write_commands.cpp @@ -59,6 +59,7 @@ #include "mongo/db/repl/replication_coordinator.h" #include "mongo/db/repl/tenant_migration_access_blocker_registry.h" #include "mongo/db/repl/tenant_migration_conflict_info.h" +#include "mongo/db/repl/tenant_migration_decoration.h" #include "mongo/db/retryable_writes_stats.h" #include "mongo/db/stats/counters.h" #include "mongo/db/storage/duplicate_key_error_info.h" @@ -127,7 +128,9 @@ const int kTimeseriesControlVersion = 1; * Transforms a single time-series insert to an update request on an existing bucket. */ write_ops::UpdateOpEntry makeTimeseriesUpdateOpEntry( - std::shared_ptr<BucketCatalog::WriteBatch> batch, const BSONObj& metadata) { + OperationContext* opCtx, + std::shared_ptr<BucketCatalog::WriteBatch> batch, + const BSONObj& metadata) { BSONObjBuilder updateBuilder; { if (!batch->min().isEmpty() || !batch->max().isEmpty()) { @@ -183,7 +186,10 @@ write_ops::UpdateOpEntry makeTimeseriesUpdateOpEntry( } } } - write_ops::UpdateModification u(updateBuilder.obj(), write_ops::UpdateModification::DiffTag{}); + write_ops::UpdateModification::DiffOptions options; + options.mustCheckExistenceForInsertOperations = + static_cast<bool>(repl::tenantMigrationRecipientInfo(opCtx)); + write_ops::UpdateModification u(updateBuilder.obj(), options); write_ops::UpdateOpEntry update(BSON("_id" << batch->bucket()->id()), std::move(u)); invariant(!update.getMulti(), batch->bucket()->id().toString()); invariant(!update.getUpsert(), batch->bucket()->id().toString()); @@ -581,11 +587,13 @@ public: } write_ops::UpdateCommandRequest _makeTimeseriesUpdateOp( + OperationContext* opCtx, std::shared_ptr<BucketCatalog::WriteBatch> batch, const BSONObj& metadata, std::vector<StmtId>&& stmtIds) const { - write_ops::UpdateCommandRequest op(ns().makeTimeseriesBucketsNamespace(), - {makeTimeseriesUpdateOpEntry(batch, metadata)}); + write_ops::UpdateCommandRequest op( + ns().makeTimeseriesBucketsNamespace(), + {makeTimeseriesUpdateOpEntry(opCtx, batch, metadata)}); op.setWriteCommandRequestBase(_makeTimeseriesWriteOpBase(std::move(stmtIds))); return op; } @@ -616,7 +624,7 @@ public: return _getTimeseriesSingleWriteResult(write_ops_exec::performUpdates( opCtx, - _makeTimeseriesUpdateOp(batch, metadata, std::move(stmtIds)), + _makeTimeseriesUpdateOp(opCtx, batch, metadata, std::move(stmtIds)), OperationSource::kTimeseries)); } @@ -716,7 +724,7 @@ public: batch, metadata, std::move(stmtIds[batch.get()->bucket()]))); } else { updateOps.push_back(_makeTimeseriesUpdateOp( - batch, metadata, std::move(stmtIds[batch.get()->bucket()]))); + opCtx, batch, metadata, std::move(stmtIds[batch.get()->bucket()]))); } } diff --git a/src/mongo/db/ops/write_ops.cpp b/src/mongo/db/ops/write_ops.cpp index f69ad8844bb..aefe6a20a4c 100644 --- a/src/mongo/db/ops/write_ops.cpp +++ b/src/mongo/db/ops/write_ops.cpp @@ -270,7 +270,7 @@ void DeleteOp::validate(const DeleteCommandRequest& deleteOp) { } write_ops::UpdateModification write_ops::UpdateModification::parseFromOplogEntry( - const BSONObj& oField) { + const BSONObj& oField, const DiffOptions& options) { BSONElement vField = oField[kUpdateOplogEntryVersionFieldName]; // If this field appears it should be an integer. @@ -288,7 +288,7 @@ write_ops::UpdateModification write_ops::UpdateModification::parseFromOplogEntry << diff.type(), diff.type() == BSONType::Object); - return UpdateModification(doc_diff::Diff{diff.embeddedObject()}, DiffTag{}); + return UpdateModification(doc_diff::Diff{diff.embeddedObject()}, options); } else if (!vField.ok() || vField.numberInt() == static_cast<int>(UpdateOplogEntryVersion::kUpdateNodeV1)) { // Treat it as a "classic" update which can either be a full replacement or a @@ -303,8 +303,8 @@ write_ops::UpdateModification write_ops::UpdateModification::parseFromOplogEntry << vField.numberInt()); } -write_ops::UpdateModification::UpdateModification(doc_diff::Diff diff, DiffTag) - : _update(std::move(diff)) {} +write_ops::UpdateModification::UpdateModification(doc_diff::Diff diff, DiffOptions options) + : _update(DeltaUpdate{std::move(diff), options}) {} write_ops::UpdateModification::UpdateModification(BSONElement update) { const auto type = update.type(); @@ -361,7 +361,7 @@ int write_ops::UpdateModification::objsize() const { return size + kWriteCommandBSONArrayPerElementOverheadBytes; }, - [](const doc_diff::Diff& diff) -> int { return diff.objsize(); }}, + [](const DeltaUpdate& delta) -> int { return delta.diff.objsize(); }}, _update); } @@ -371,7 +371,7 @@ write_ops::UpdateModification::Type write_ops::UpdateModification::type() const visit_helper::Overloaded{ [](const ClassicUpdate& classic) { return Type::kClassic; }, [](const PipelineUpdate& pipelineUpdate) { return Type::kPipeline; }, - [](const doc_diff::Diff& diff) { return Type::kDelta; }}, + [](const DeltaUpdate& delta) { return Type::kDelta; }}, _update); } @@ -392,7 +392,7 @@ void write_ops::UpdateModification::serializeToBSON(StringData fieldName, } arrayBuilder.doneFast(); }, - [fieldName, bob](const doc_diff::Diff& diff) { *bob << fieldName << diff; }}, + [fieldName, bob](const DeltaUpdate& delta) { *bob << fieldName << delta.diff; }}, _update); } diff --git a/src/mongo/db/ops/write_ops_exec.cpp b/src/mongo/db/ops/write_ops_exec.cpp index 2c6f8eb07e3..929d8990828 100644 --- a/src/mongo/db/ops/write_ops_exec.cpp +++ b/src/mongo/db/ops/write_ops_exec.cpp @@ -1193,10 +1193,13 @@ Status performAtomicTimeseriesWrites( } auto original = record->data.toBson(); + const bool mustCheckExistenceForInsertOperations = + static_cast<bool>(repl::tenantMigrationRecipientInfo(opCtx)); auto [updated, indexesAffected] = doc_diff::applyDiff(original, update.getU().getDiff(), - &CollectionQueryInfo::get(*coll).getIndexKeys(opCtx)); + &CollectionQueryInfo::get(*coll).getIndexKeys(opCtx), + mustCheckExistenceForInsertOperations); CollectionUpdateArgs args; if (const auto& stmtIds = op.getStmtIds()) { diff --git a/src/mongo/db/ops/write_ops_parsers.h b/src/mongo/db/ops/write_ops_parsers.h index 900740b271b..22608793005 100644 --- a/src/mongo/db/ops/write_ops_parsers.h +++ b/src/mongo/db/ops/write_ops_parsers.h @@ -84,23 +84,27 @@ public: /** * Used to indicate that a certain type of update is being passed to the constructor. */ - struct DiffTag {}; + struct DiffOptions { + bool mustCheckExistenceForInsertOperations = true; + }; struct ClassicTag {}; // Given the 'o' field of an update oplog entry, will return an UpdateModification that can be - // applied. - static UpdateModification parseFromOplogEntry(const BSONObj& oField); + // applied. The `options` parameter will be applied only in the case a Delta update is parsed. + static UpdateModification parseFromOplogEntry(const BSONObj& oField, + const DiffOptions& options); static UpdateModification parseFromClassicUpdate(const BSONObj& modifiers) { return UpdateModification(modifiers, ClassicTag{}); } - static UpdateModification parseFromV2Delta(const doc_diff::Diff& diff) { - return UpdateModification(diff, DiffTag{}); + static UpdateModification parseFromV2Delta(const doc_diff::Diff& diff, + DiffOptions const& options) { + return UpdateModification(diff, options); } UpdateModification() = default; UpdateModification(BSONElement update); UpdateModification(std::vector<BSONObj> pipeline); - UpdateModification(doc_diff::Diff, DiffTag); + UpdateModification(doc_diff::Diff, DiffOptions); // This constructor exists only to provide a fast-path for constructing classic-style updates. UpdateModification(const BSONObj& update, ClassicTag); @@ -143,7 +147,12 @@ public: doc_diff::Diff getDiff() const { invariant(type() == Type::kDelta); - return stdx::get<doc_diff::Diff>(_update); + return stdx::get<DeltaUpdate>(_update).diff; + } + + bool mustCheckExistenceForInsertOperations() const { + invariant(type() == Type::kDelta); + return stdx::get<DeltaUpdate>(_update).options.mustCheckExistenceForInsertOperations; } std::string toString() const { @@ -157,8 +166,9 @@ public: sb << "{type: Pipeline, update: " << Value(pipeline).toString() << "}"; }, - [&sb](const doc_diff::Diff& diff) { - sb << "{type: Delta, update: " << diff << "}"; + [&sb](const DeltaUpdate& delta) { + sb << "{type: Delta, update: " << delta.diff + << "}"; }}, _update); @@ -171,7 +181,11 @@ private: BSONObj bson; }; using PipelineUpdate = std::vector<BSONObj>; - stdx::variant<ClassicUpdate, PipelineUpdate, doc_diff::Diff> _update; + struct DeltaUpdate { + doc_diff::Diff diff; + DiffOptions options; + }; + stdx::variant<ClassicUpdate, PipelineUpdate, DeltaUpdate> _update; }; } // namespace write_ops diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp index 33d35adb97f..996f3bbcc4d 100644 --- a/src/mongo/db/repl/oplog.cpp +++ b/src/mongo/db/repl/oplog.cpp @@ -1364,7 +1364,17 @@ Status applyOperation_inlock(OperationContext* opCtx, auto request = UpdateRequest(); request.setNamespaceString(requestNss); request.setQuery(updateCriteria); - auto updateMod = write_ops::UpdateModification::parseFromOplogEntry(o); + // If we are in steady state and the update is on a timeseries bucket collection, we can + // enable some optimizations in diff application. In some cases, during tenant + // migration, we can for some reason generate entries for timeseries bucket collections + // which still rely on the idempotency guarantee, which then means we shouldn't apply + // these optimizations. + write_ops::UpdateModification::DiffOptions options; + if (mode == OplogApplication::Mode::kSecondary && collection->getTimeseriesOptions() && + !op.getFromTenantMigration()) { + options.mustCheckExistenceForInsertOperations = false; + } + auto updateMod = write_ops::UpdateModification::parseFromOplogEntry(o, options); // TODO SERVER-51075: Remove FCV checks for $v:2 delta oplog entries. if (updateMod.type() == write_ops::UpdateModification::Type::kDelta) { diff --git a/src/mongo/db/s/resharding/resharding_oplog_application.cpp b/src/mongo/db/s/resharding/resharding_oplog_application.cpp index 88da6c465a0..25c14032d7e 100644 --- a/src/mongo/db/s/resharding/resharding_oplog_application.cpp +++ b/src/mongo/db/s/resharding/resharding_oplog_application.cpp @@ -323,7 +323,8 @@ void ReshardingOplogApplicationRules::_applyUpdate_inlock(OperationContext* opCt !idField.eoo()); BSONObj idQuery = idField.wrap(); - auto updateMod = write_ops::UpdateModification::parseFromOplogEntry(oField); + auto updateMod = write_ops::UpdateModification::parseFromOplogEntry( + oField, write_ops::UpdateModification::DiffOptions{}); // First, query the conflict stash collection using [op _id] as the query. If a doc exists, // apply rule #1 and update the doc from the stash collection. diff --git a/src/mongo/db/update/delta_executor.cpp b/src/mongo/db/update/delta_executor.cpp index c1d4eeb9f1e..983a3161d90 100644 --- a/src/mongo/db/update/delta_executor.cpp +++ b/src/mongo/db/update/delta_executor.cpp @@ -41,7 +41,8 @@ DeltaExecutor::ApplyResult DeltaExecutor::applyUpdate( UpdateExecutor::ApplyParams applyParams) const { const auto originalDoc = applyParams.element.getDocument().getObject(); - auto applyDiffOutput = doc_diff::applyDiff(originalDoc, _diff, applyParams.indexData); + auto applyDiffOutput = doc_diff::applyDiff( + originalDoc, _diff, applyParams.indexData, _mustCheckExistenceForInsertOperations); const auto& postImage = applyDiffOutput.postImage; auto postImageHasId = postImage.hasField("_id"); diff --git a/src/mongo/db/update/delta_executor.h b/src/mongo/db/update/delta_executor.h index d3ba6a9c199..db1ed84a7a6 100644 --- a/src/mongo/db/update/delta_executor.h +++ b/src/mongo/db/update/delta_executor.h @@ -46,9 +46,10 @@ public: /** * Initializes the executor with the diff to apply. */ - explicit DeltaExecutor(doc_diff::Diff diff) + explicit DeltaExecutor(doc_diff::Diff diff, bool mustCheckExistenceForInsertOperations) : _diff(std::move(diff)), - _outputOplogEntry(update_oplog_entry::makeDeltaOplogEntry(_diff)) {} + _outputOplogEntry(update_oplog_entry::makeDeltaOplogEntry(_diff)), + _mustCheckExistenceForInsertOperations(mustCheckExistenceForInsertOperations) {} ApplyResult applyUpdate(ApplyParams applyParams) const final; @@ -65,6 +66,8 @@ private: // still needs to produce an oplog entry from the applyUpdate() method so that OpObservers may // handle the event appropriately. BSONObj _outputOplogEntry; + + const bool _mustCheckExistenceForInsertOperations; }; } // namespace mongo diff --git a/src/mongo/db/update/delta_executor_test.cpp b/src/mongo/db/update/delta_executor_test.cpp index 849f615d6bb..c1860d10ca8 100644 --- a/src/mongo/db/update/delta_executor_test.cpp +++ b/src/mongo/db/update/delta_executor_test.cpp @@ -43,6 +43,7 @@ namespace { TEST(DeltaExecutorTest, Delete) { BSONObj preImage(fromjson("{f1: {a: {b: {c: 1}, c: 1}}}")); UpdateIndexData indexData; + constexpr bool mustCheckExistenceForInsertOperations = true; indexData.addPath(FieldRef("p.a.b")); indexData.addPath(FieldRef("f1.a.b")); FieldRefSet fieldRefSet; @@ -51,7 +52,8 @@ TEST(DeltaExecutorTest, Delete) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - DeltaExecutor test(fromjson("{d: {f1: false, f2: false, f3: false}}")); + DeltaExecutor test(fromjson("{d: {f1: false, f2: false, f3: false}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), BSONObj()); ASSERT(result.indexesAffected); @@ -62,7 +64,8 @@ TEST(DeltaExecutorTest, Delete) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - DeltaExecutor test(fromjson("{sf1: {sa: {d: {p: false, c: false, b: false}}}}")); + DeltaExecutor test(fromjson("{sf1: {sa: {d: {p: false, c: false, b: false}}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: {a: {}}}")); @@ -73,7 +76,8 @@ TEST(DeltaExecutorTest, Delete) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - auto test = DeltaExecutor(fromjson("{sf1: {sa: {sb: {d: {c: false}}}}}")); + auto test = DeltaExecutor(fromjson("{sf1: {sa: {sb: {d: {c: false}}}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: {a: {b: {}, c: 1}}}")); @@ -84,7 +88,8 @@ TEST(DeltaExecutorTest, Delete) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - auto test = DeltaExecutor(fromjson("{sf1: {sa: {d: {c: false}}}}")); + auto test = DeltaExecutor(fromjson("{sf1: {sa: {d: {c: false}}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: {a: {b: {c: 1}}}}")); @@ -95,6 +100,7 @@ TEST(DeltaExecutorTest, Delete) { TEST(DeltaExecutorTest, Update) { BSONObj preImage(fromjson("{f1: {a: {b: {c: 1}, c: 1}}}")); UpdateIndexData indexData; + constexpr bool mustCheckExistenceForInsertOperations = true; indexData.addPath(FieldRef("p.a.b")); indexData.addPath(FieldRef("f1.a.b")); FieldRefSet fieldRefSet; @@ -103,7 +109,8 @@ TEST(DeltaExecutorTest, Update) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - DeltaExecutor test(fromjson("{u: {f1: false, f2: false, f3: false}}")); + DeltaExecutor test(fromjson("{u: {f1: false, f2: false, f3: false}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: false, f2: false, f3: false}")); @@ -114,7 +121,8 @@ TEST(DeltaExecutorTest, Update) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - auto test = DeltaExecutor(fromjson("{sf1: {sa: {u: {p: false, c: false, b: false}}}}")); + auto test = DeltaExecutor(fromjson("{sf1: {sa: {u: {p: false, c: false, b: false}}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: {a: {b: false, c: false, p: false}}}")); @@ -125,7 +133,8 @@ TEST(DeltaExecutorTest, Update) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - auto test = DeltaExecutor(fromjson("{sf1: {sa: {sb: {u: {c: false}}}}}")); + auto test = DeltaExecutor(fromjson("{sf1: {sa: {sb: {u: {c: false}}}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: {a: {b: {c: false}, c: 1}}}")); @@ -136,7 +145,8 @@ TEST(DeltaExecutorTest, Update) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - auto test = DeltaExecutor(fromjson("{sf1: {sa: {u: {c: false}}}}")); + auto test = DeltaExecutor(fromjson("{sf1: {sa: {u: {c: false}}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: {a: {b: {c: 1}, c: false}}}")); @@ -146,6 +156,7 @@ TEST(DeltaExecutorTest, Update) { TEST(DeltaExecutorTest, Insert) { UpdateIndexData indexData; + constexpr bool mustCheckExistenceForInsertOperations = true; indexData.addPath(FieldRef("p.a.b")); // 'UpdateIndexData' will canonicalize the path and remove all numeric components. So the '2' // and '33' components should not matter. @@ -156,7 +167,8 @@ TEST(DeltaExecutorTest, Insert) { auto doc = mutablebson::Document(BSONObj()); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - DeltaExecutor test(fromjson("{i: {f1: false, f2: false, f3: false}}")); + DeltaExecutor test(fromjson("{i: {f1: false, f2: false, f3: false}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: false, f2: false, f3: false}")); @@ -167,7 +179,8 @@ TEST(DeltaExecutorTest, Insert) { auto doc = mutablebson::Document(fromjson("{f1: {a: {c: true}}}}")); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - auto test = DeltaExecutor(fromjson("{sf1: {sa: {i: {p: false, c: false, b: false}}}}")); + auto test = DeltaExecutor(fromjson("{sf1: {sa: {i: {p: false, c: false, b: false}}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: {a: {p: false, c: false, b: false}}}")); @@ -178,7 +191,8 @@ TEST(DeltaExecutorTest, Insert) { auto doc = mutablebson::Document(fromjson("{f1: {a: {b: {c: {e: 1}}}}}")); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - auto test = DeltaExecutor(fromjson("{sf1: {sa: {sb: {sc: {i : {d: 2} }}}}}")); + auto test = DeltaExecutor(fromjson("{sf1: {sa: {sb: {sc: {i : {d: 2} }}}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: {a: {b: {c: {e: 1, d: 2}}}}}")); @@ -189,7 +203,8 @@ TEST(DeltaExecutorTest, Insert) { auto doc = mutablebson::Document(fromjson("{f1: {a: {b: {c: 1}}}}")); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - auto test = DeltaExecutor(fromjson("{sf1: {sa: {i: {c: 2}}}}")); + auto test = DeltaExecutor(fromjson("{sf1: {sa: {i: {c: 2}}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: {a: {b: {c: 1}, c: 2}}}")); @@ -200,6 +215,7 @@ TEST(DeltaExecutorTest, Insert) { TEST(DeltaExecutorTest, InsertNumericFieldNamesTopLevel) { BSONObj preImage; UpdateIndexData indexData; + constexpr bool mustCheckExistenceForInsertOperations = true; indexData.addPath(FieldRef{"1"}); FieldRefSet fieldRefSet; @@ -209,7 +225,8 @@ TEST(DeltaExecutorTest, InsertNumericFieldNamesTopLevel) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - DeltaExecutor test(fromjson("{i: {'0': false, '1': false, '2': false}}")); + DeltaExecutor test(fromjson("{i: {'0': false, '1': false, '2': false}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{'0': false, '1': false, '2': false}")); @@ -219,7 +236,8 @@ TEST(DeltaExecutorTest, InsertNumericFieldNamesTopLevel) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - DeltaExecutor test(fromjson("{i: {'0': false, '2': false}}")); + DeltaExecutor test(fromjson("{i: {'0': false, '2': false}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{'0': false, '2': false}")); @@ -230,6 +248,7 @@ TEST(DeltaExecutorTest, InsertNumericFieldNamesTopLevel) { TEST(DeltaExecutorTest, InsertNumericFieldNamesNested) { BSONObj preImage{fromjson("{a: {}}")}; UpdateIndexData indexData; + constexpr bool mustCheckExistenceForInsertOperations = true; indexData.addPath(FieldRef{"a.1"}); FieldRefSet fieldRefSet; @@ -239,7 +258,8 @@ TEST(DeltaExecutorTest, InsertNumericFieldNamesNested) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - DeltaExecutor test(fromjson("{sa: {i: {'0': false, '1': false, '2': false}}}")); + DeltaExecutor test(fromjson("{sa: {i: {'0': false, '1': false, '2': false}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{a: {'0': false, '1': false, '2': false}}")); @@ -249,7 +269,8 @@ TEST(DeltaExecutorTest, InsertNumericFieldNamesNested) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - DeltaExecutor test(fromjson("{sa: {i: {'0': false, '2': false}}}")); + DeltaExecutor test(fromjson("{sa: {i: {'0': false, '2': false}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{a: {'0': false, '2': false}}")); @@ -260,6 +281,7 @@ TEST(DeltaExecutorTest, InsertNumericFieldNamesNested) { TEST(DeltaExecutorTest, ArraysInIndexPath) { BSONObj preImage(fromjson("{f1: [{a: {b: {c: 1}, c: 1}}, 1]}")); UpdateIndexData indexData; + constexpr bool mustCheckExistenceForInsertOperations = true; indexData.addPath(FieldRef("p.a.b")); // Numeric components will be ignored, so they should not matter. indexData.addPath(FieldRef("f1.9.a.10.b")); @@ -269,7 +291,8 @@ TEST(DeltaExecutorTest, ArraysInIndexPath) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - DeltaExecutor test(fromjson("{sf1: {a: true, l: 1}}")); + DeltaExecutor test(fromjson("{sf1: {a: true, l: 1}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: [{a: {b: {c: 1}, c: 1}}]}")); @@ -281,7 +304,8 @@ TEST(DeltaExecutorTest, ArraysInIndexPath) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - auto test = DeltaExecutor(fromjson("{sf1: {a: true, s0: {sa: {sb: {i: {d: 1} }}}}}")); + auto test = DeltaExecutor(fromjson("{sf1: {a: true, s0: {sa: {sb: {i: {d: 1} }}}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: [{a: {b: {c: 1, d: 1}, c: 1}}, 1]}")); @@ -292,7 +316,8 @@ TEST(DeltaExecutorTest, ArraysInIndexPath) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - auto test = DeltaExecutor(fromjson("{sf1: {a: true, u2: {b: 1}}}")); + auto test = DeltaExecutor(fromjson("{sf1: {a: true, u2: {b: 1}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: [{a: {b: {c: 1}, c: 1}}, 1, {b:1}]}")); @@ -303,7 +328,8 @@ TEST(DeltaExecutorTest, ArraysInIndexPath) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - auto test = DeltaExecutor(fromjson("{sf1: {a: true, s0: {sa: {d: {c: false} }}}}")); + auto test = DeltaExecutor(fromjson("{sf1: {a: true, s0: {sa: {d: {c: false} }}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: [{a: {b: {c: 1}}}, 1]}")); @@ -314,6 +340,7 @@ TEST(DeltaExecutorTest, ArraysInIndexPath) { TEST(DeltaExecutorTest, ArraysAfterIndexPath) { BSONObj preImage(fromjson("{f1: {a: {b: [{c: 1}, 2]}}}")); UpdateIndexData indexData; + constexpr bool mustCheckExistenceForInsertOperations = true; indexData.addPath(FieldRef("p.a.b")); // 'UpdateIndexData' will canonicalize the path and remove all numeric components. So the '9' // and '10' components should not matter. @@ -324,7 +351,8 @@ TEST(DeltaExecutorTest, ArraysAfterIndexPath) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - DeltaExecutor test(fromjson("{sf1: {sa: {sb: {a: true, l: 1}}}}")); + DeltaExecutor test(fromjson("{sf1: {sa: {sb: {a: true, l: 1}}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: {a: {b: [{c: 1}]}}}")); @@ -335,7 +363,8 @@ TEST(DeltaExecutorTest, ArraysAfterIndexPath) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - auto test = DeltaExecutor(fromjson("{sf1: {sa: {sb: {a: true, s0: {u: {c: 2}} }}}}")); + auto test = DeltaExecutor(fromjson("{sf1: {sa: {sb: {a: true, s0: {u: {c: 2}} }}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: {a: {b: [{c: 2}, 2]}}}")); @@ -346,7 +375,8 @@ TEST(DeltaExecutorTest, ArraysAfterIndexPath) { auto doc = mutablebson::Document(preImage); UpdateExecutor::ApplyParams params(doc.root(), fieldRefSet); params.indexData = &indexData; - auto test = DeltaExecutor(fromjson("{sf1: {sa: {sb: {a: true, u0: 1 }}}}")); + auto test = DeltaExecutor(fromjson("{sf1: {sa: {sb: {a: true, u0: 1 }}}}"), + mustCheckExistenceForInsertOperations); auto result = test.applyUpdate(params); ASSERT_BSONOBJ_BINARY_EQ(params.element.getDocument().getObject(), fromjson("{f1: {a: {b: [1, 2]}}}")); diff --git a/src/mongo/db/update/document_diff_applier.cpp b/src/mongo/db/update/document_diff_applier.cpp index a9dc9c272a0..a7d9145b54d 100644 --- a/src/mongo/db/update/document_diff_applier.cpp +++ b/src/mongo/db/update/document_diff_applier.cpp @@ -74,37 +74,47 @@ struct DocumentDiffTables { // Order in which new fields should be added to the pre image. std::vector<BSONElement> fieldsToInsert; + std::size_t sizeOfFieldsToInsert = 0; + // Diff only inserts fields, no deletes or updates + bool insertOnly = false; }; DocumentDiffTables buildObjDiffTables(DocumentDiffReader* reader) { DocumentDiffTables out; + out.insertOnly = true; boost::optional<StringData> optFieldName; while ((optFieldName = reader->nextDelete())) { out.safeInsert(*optFieldName, Delete{}); + out.insertOnly = false; } boost::optional<BSONElement> nextUpdate; while ((nextUpdate = reader->nextUpdate())) { out.safeInsert(nextUpdate->fieldNameStringData(), Update{*nextUpdate}); out.fieldsToInsert.push_back(*nextUpdate); + out.insertOnly = false; } boost::optional<BSONElement> nextInsert; while ((nextInsert = reader->nextInsert())) { out.safeInsert(nextInsert->fieldNameStringData(), Insert{*nextInsert}); out.fieldsToInsert.push_back(*nextInsert); + out.sizeOfFieldsToInsert += out.fieldsToInsert.back().size(); } for (auto next = reader->nextSubDiff(); next; next = reader->nextSubDiff()) { out.safeInsert(next->first, SubDiff{next->second}); + out.insertOnly = false; } return out; } class DiffApplier { public: - DiffApplier(const UpdateIndexData* indexData) : _indexData(indexData) {} + DiffApplier(const UpdateIndexData* indexData, bool mustCheckExistenceForInsertOperations) + : _indexData(indexData), + _mustCheckExistenceForInsertOperations{mustCheckExistenceForInsertOperations} {} void applyDiffToObject(const BSONObj& preImage, FieldRef* path, @@ -114,6 +124,17 @@ public: // the diff again once this is done. const DocumentDiffTables tables = buildObjDiffTables(reader); + if (!_mustCheckExistenceForInsertOperations && tables.insertOnly) { + builder->bb().reserveBytes(preImage.objsize() + tables.sizeOfFieldsToInsert); + builder->appendElements(preImage); + for (auto&& elt : tables.fieldsToInsert) { + builder->append(elt); + FieldRef::FieldRefTempAppend tempAppend(*path, elt.fieldNameStringData()); + updateIndexesAffected(path); + } + return; + } + // Keep track of what fields we already appended, so that we can insert the rest at the end. StringDataSet fieldsToSkipInserting; @@ -316,14 +337,18 @@ private: } const UpdateIndexData* _indexData; + bool _mustCheckExistenceForInsertOperations = true; bool _indexesAffected = false; }; } // namespace -ApplyDiffOutput applyDiff(const BSONObj& pre, const Diff& diff, const UpdateIndexData* indexData) { +ApplyDiffOutput applyDiff(const BSONObj& pre, + const Diff& diff, + const UpdateIndexData* indexData, + bool mustCheckExistenceForInsertOperations) { DocumentDiffReader reader(diff); BSONObjBuilder out; - DiffApplier applier(indexData); + DiffApplier applier(indexData, mustCheckExistenceForInsertOperations); FieldRef path; applier.applyDiffToObject(pre, &path, &reader, &out); return {out.obj(), applier.indexesAffected()}; diff --git a/src/mongo/db/update/document_diff_applier.h b/src/mongo/db/update/document_diff_applier.h index 22ebfce5247..4a45f47d4c1 100644 --- a/src/mongo/db/update/document_diff_applier.h +++ b/src/mongo/db/update/document_diff_applier.h @@ -43,8 +43,15 @@ struct ApplyDiffOutput { /** * Applies the diff to 'pre' and returns the post image. Throws if the diff is invalid. The - * indexData' parameter is optional, if provided computes whether the indexes are affected. + * 'indexData' parameter is optional, if provided computes whether the indexes are affected. + * The final, 'mustCheckExistenceForInsertOperations' parameter signals whether we must check if an + * inserted field already exists within a (sub)document. This should generally be set to true, + * unless the caller has knowledge of the pre-image and the diff, and can guarantee that we will not + * re-insert anything. */ -ApplyDiffOutput applyDiff(const BSONObj& pre, const Diff& diff, const UpdateIndexData* indexData); +ApplyDiffOutput applyDiff(const BSONObj& pre, + const Diff& diff, + const UpdateIndexData* indexData, + bool mustCheckExistenceForInsertOperations); } // namespace doc_diff } // namespace mongo diff --git a/src/mongo/db/update/document_diff_test.cpp b/src/mongo/db/update/document_diff_test.cpp index 87e9aa594a6..b4b02c9f55c 100644 --- a/src/mongo/db/update/document_diff_test.cpp +++ b/src/mongo/db/update/document_diff_test.cpp @@ -99,55 +99,108 @@ std::vector<BSONObj> getDocumentsRepo() { return documents; } -void runTest(std::vector<BSONObj> documents, size_t numSimulations) { +std::vector<BSONObj> getDocumentsRepoAppendOnly() { + const static std::vector<BSONObj> documents = { + createObjWithLargePrefix( + "{field1: {level1Field1: {level1Field1: 1}}, field2: {level1Field1: {}}, field3: " + "{level1Field3: 'va2'}, field4: ['arrayVal1']}"), + createObjWithLargePrefix( + "{field1: {level1Field1: {level1Field1: 1}, level1Field2: 'val2'}, field2: " + "{level1Field1: {level2Field1: {}}}, field3: {level1Field3: 'va2'}, field4: " + "['arrayVal1', 'arrayVal2', 'arrayVal3', 'arrayVal4']}"), + createObjWithLargePrefix( + "{field1: {level1Field1: {level1Field1: 1}, level1Field2: 'val2'}, field2: " + "{level1Field1: {level2Field1: {}}}, field3: {level1Field3: 'va2', level1Field4: " + "'va4'}, field4: ['arrayVal1', 'arrayVal2', 'arrayVal3', 'arrayVal4', 'arrayVal5']}, " + "field5: {}, field6: 'va6'"), + }; + return documents; +} + +struct TestOptions { + std::vector<BSONObj> documents; + size_t numSimulations = 10; + bool shuffle = true; + bool mustCheckExistenceForInsertOperations = true; +}; + +void runTest(TestOptions* options) { // Shuffle them into a random order auto rng = getRNG(); - LOGV2(4785301, "Seed used for the test ", "seed"_attr = getSeed()); - for (size_t simulation = 0; simulation < numSimulations; ++simulation) { - std::shuffle(documents.begin(), documents.end(), rng->urbg()); + if (options->shuffle) { + LOGV2(4785301, "Seed used for the test ", "seed"_attr = getSeed()); + } + for (size_t simulation = 0; simulation < options->numSimulations; ++simulation) { + if (options->shuffle) { + std::shuffle(options->documents.begin(), options->documents.end(), rng->urbg()); + } - auto preDoc = documents[0]; + auto preDoc = options->documents[0]; std::vector<BSONObj> diffs; - diffs.reserve(documents.size() - 1); - for (size_t i = 1; i < documents.size(); ++i) { - const auto diffOutput = computeDiff( - preDoc, documents[i], update_oplog_entry::kSizeOfDeltaOplogEntryMetadata, nullptr); + diffs.reserve(options->documents.size() - 1); + for (size_t i = 1; i < options->documents.size(); ++i) { + const auto diffOutput = computeDiff(preDoc, + options->documents[i], + update_oplog_entry::kSizeOfDeltaOplogEntryMetadata, + nullptr); ASSERT(diffOutput); diffs.push_back(diffOutput->diff); - const auto postObj = applyDiffTestHelper(preDoc, diffOutput->diff); - ASSERT_BSONOBJ_BINARY_EQ(documents[i], postObj); - - // Applying the diff the second time also generates the same object. - ASSERT_BSONOBJ_BINARY_EQ(postObj, applyDiffTestHelper(postObj, diffOutput->diff)); + const auto postObj = applyDiffTestHelper( + preDoc, diffOutput->diff, options->mustCheckExistenceForInsertOperations); + ASSERT_BSONOBJ_BINARY_EQ(options->documents[i], postObj); + + if (options->mustCheckExistenceForInsertOperations) { + // Applying the diff the second time also generates the same object. + ASSERT_BSONOBJ_BINARY_EQ( + postObj, + applyDiffTestHelper( + postObj, diffOutput->diff, options->mustCheckExistenceForInsertOperations)); + } - preDoc = documents[i]; + preDoc = options->documents[i]; } // Verify that re-applying any suffix of the diffs in the sequence order will end produce // the same end state. - for (size_t start = 0; start < diffs.size(); ++start) { - auto endObj = documents.back(); - for (size_t i = start; i < diffs.size(); ++i) { - endObj = applyDiffTestHelper(endObj, diffs[i]); + if (options->mustCheckExistenceForInsertOperations) { + for (size_t start = 0; start < diffs.size(); ++start) { + auto endObj = options->documents.back(); + for (size_t i = start; i < diffs.size(); ++i) { + endObj = applyDiffTestHelper( + endObj, diffs[i], options->mustCheckExistenceForInsertOperations); + } + ASSERT_BSONOBJ_BINARY_EQ(endObj, options->documents.back()); } - ASSERT_BSONOBJ_BINARY_EQ(endObj, documents.back()); } } } + TEST(DocumentDiffTest, PredefinedDocumentsTest) { - runTest(getDocumentsRepo(), 10); + TestOptions options; + options.documents = getDocumentsRepo(); + runTest(&options); +} + +TEST(DocumentDiffTest, PredefinedDocumentsAppendOnlyTest) { + TestOptions options; + options.documents = getDocumentsRepoAppendOnly(); + options.shuffle = false; + options.numSimulations = 1; + options.mustCheckExistenceForInsertOperations = false; + runTest(&options); } TEST(DocumentDiffTest, RandomizedDocumentBuilderTest) { + TestOptions options; const auto numDocs = 20; - std::vector<BSONObj> documents(numDocs); + options.documents.reserve(numDocs); auto rng = getRNG(); for (int i = 0; i < numDocs; ++i) { MutableDocument doc; - documents[i] = generateDoc(rng, &doc, 0); + options.documents.emplace_back(generateDoc(rng, &doc, 0)); } - runTest(std::move(documents), 10); + runTest(&options); } } // namespace diff --git a/src/mongo/db/update/document_diff_test_helpers.cpp b/src/mongo/db/update/document_diff_test_helpers.cpp index 7082025fad9..be2d05ab8d6 100644 --- a/src/mongo/db/update/document_diff_test_helpers.cpp +++ b/src/mongo/db/update/document_diff_test_helpers.cpp @@ -118,9 +118,11 @@ BSONObj generateDoc(PseudoRandom* rng, MutableDocument* doc, int depthLevel) { return doc->freeze().toBson(); } -BSONObj applyDiffTestHelper(BSONObj preImage, BSONObj diff) { +BSONObj applyDiffTestHelper(BSONObj preImage, + BSONObj diff, + bool mustCheckExistenceForInsertOperations) { UpdateIndexData indexData; - return applyDiff(preImage, diff, &indexData).postImage; + return applyDiff(preImage, diff, &indexData, mustCheckExistenceForInsertOperations).postImage; } } // namespace mongo::doc_diff diff --git a/src/mongo/db/update/document_diff_test_helpers.h b/src/mongo/db/update/document_diff_test_helpers.h index 8ba9aea35a3..e5433014b9b 100644 --- a/src/mongo/db/update/document_diff_test_helpers.h +++ b/src/mongo/db/update/document_diff_test_helpers.h @@ -41,5 +41,7 @@ BSONObj createObjWithLargePrefix(const std::string& suffix); BSONObj generateDoc(PseudoRandom* rng, MutableDocument* doc, int depthLevel); -BSONObj applyDiffTestHelper(BSONObj preImage, BSONObj diff); +BSONObj applyDiffTestHelper(BSONObj preImage, + BSONObj diff, + bool mustCheckExistenceForInsertOperations = true); } // namespace mongo::doc_diff diff --git a/src/mongo/db/update/update_driver.cpp b/src/mongo/db/update/update_driver.cpp index eecd380ebe2..50340b57864 100644 --- a/src/mongo/db/update/update_driver.cpp +++ b/src/mongo/db/update/update_driver.cpp @@ -145,7 +145,8 @@ void UpdateDriver::parse( arrayFilters.empty()); _updateType = UpdateType::kDelta; - _updateExecutor = std::make_unique<DeltaExecutor>(updateMod.getDiff()); + _updateExecutor = std::make_unique<DeltaExecutor>( + updateMod.getDiff(), updateMod.mustCheckExistenceForInsertOperations()); return; } diff --git a/src/mongo/db/update/update_driver_test.cpp b/src/mongo/db/update/update_driver_test.cpp index f93d5343661..f133caf0d56 100644 --- a/src/mongo/db/update/update_driver_test.cpp +++ b/src/mongo/db/update/update_driver_test.cpp @@ -703,7 +703,8 @@ TEST_F(ModifiedPathsTestFixture, DeltaUpdateNotAffectingIndex) { BSONObj spec = fromjson("{d: {a: false}}"); mutablebson::Document doc(fromjson("{a: [{b: 0}]}")); runUpdate(&doc, - write_ops::UpdateModification::parseFromV2Delta(spec), + write_ops::UpdateModification::parseFromV2Delta( + spec, write_ops::UpdateModification::DiffOptions{}), ""_sd, {}, true /* fromOplog */); @@ -712,7 +713,8 @@ TEST_F(ModifiedPathsTestFixture, DeltaUpdateNotAffectingIndex) { UpdateIndexData indexData; indexData.addPath(FieldRef("p")); runUpdate(&doc, - write_ops::UpdateModification::parseFromV2Delta(spec), + write_ops::UpdateModification::parseFromV2Delta( + spec, write_ops::UpdateModification::DiffOptions{}), ""_sd, {}, true /* fromOplog */, @@ -727,7 +729,8 @@ TEST_F(ModifiedPathsTestFixture, DeltaUpdateAffectingIndex) { indexData.addPath(FieldRef("q")); indexData.addPath(FieldRef("a.p")); runUpdate(&doc, - write_ops::UpdateModification::parseFromV2Delta(spec), + write_ops::UpdateModification::parseFromV2Delta( + spec, write_ops::UpdateModification::DiffOptions{}), ""_sd, {}, true /* fromOplog */, |