summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/commands/oplog_application_checks.cpp3
-rw-r--r--src/mongo/db/commands/write_commands.cpp20
-rw-r--r--src/mongo/db/ops/write_ops.cpp14
-rw-r--r--src/mongo/db/ops/write_ops_exec.cpp5
-rw-r--r--src/mongo/db/ops/write_ops_parsers.h34
-rw-r--r--src/mongo/db/repl/oplog.cpp12
-rw-r--r--src/mongo/db/s/resharding/resharding_oplog_application.cpp3
-rw-r--r--src/mongo/db/update/delta_executor.cpp3
-rw-r--r--src/mongo/db/update/delta_executor.h7
-rw-r--r--src/mongo/db/update/delta_executor_test.cpp76
-rw-r--r--src/mongo/db/update/document_diff_applier.cpp31
-rw-r--r--src/mongo/db/update/document_diff_applier.h11
-rw-r--r--src/mongo/db/update/document_diff_test.cpp101
-rw-r--r--src/mongo/db/update/document_diff_test_helpers.cpp6
-rw-r--r--src/mongo/db/update/document_diff_test_helpers.h4
-rw-r--r--src/mongo/db/update/update_driver.cpp3
-rw-r--r--src/mongo/db/update/update_driver_test.cpp9
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 */,