summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaitlin Mahar <kaitlin.mahar@mongodb.com>2023-04-19 22:24:35 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-04-20 00:39:50 +0000
commit032c7f7d75006a6530639a80842afdd3ac13e5f6 (patch)
treec2fe8b4e02162045860ab36b5666c0d8d41caebb
parentf5b992c420e971bee2b951e3f0e7d947a3b68f2e (diff)
downloadmongo-032c7f7d75006a6530639a80842afdd3ac13e5f6.tar.gz
SERVER-74390 Add numErrors to bulkWrite response
-rw-r--r--jstests/core/write/bulk/bulk_write.js22
-rw-r--r--jstests/core/write/bulk/bulk_write_delete_cursor.js7
-rw-r--r--jstests/core/write/bulk/bulk_write_getMore.js1
-rw-r--r--jstests/core/write/bulk/bulk_write_insert_cursor.js2
-rw-r--r--jstests/core/write/bulk/bulk_write_non_retryable_cursor.js4
-rw-r--r--jstests/core/write/bulk/bulk_write_non_transaction.js87
-rw-r--r--jstests/core/write/bulk/bulk_write_update_cursor.js14
-rw-r--r--src/mongo/db/bulk_write_shard_test.cpp48
-rw-r--r--src/mongo/db/commands/bulk_write.cpp35
-rw-r--r--src/mongo/db/commands/bulk_write.h2
-rw-r--r--src/mongo/db/commands/bulk_write.idl3
-rw-r--r--src/mongo/s/commands/cluster_bulk_write_cmd.cpp12
12 files changed, 170 insertions, 67 deletions
diff --git a/jstests/core/write/bulk/bulk_write.js b/jstests/core/write/bulk/bulk_write.js
index ff6ed1eec1e..f1767bd0338 100644
--- a/jstests/core/write/bulk/bulk_write.js
+++ b/jstests/core/write/bulk/bulk_write.js
@@ -39,17 +39,21 @@ var res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
assert.eq(coll.find().itcount(), 1);
assert.eq(coll1.find().itcount(), 0);
coll.drop();
// Make sure ops and nsInfo can take arrays properly
-assert.commandWorked(db.adminCommand({
+res = db.adminCommand({
bulkWrite: 1,
ops: [{insert: 1, document: {skey: "MongoDB"}}, {insert: 0, document: {skey: "MongoDB"}}],
nsInfo: [{ns: "test.coll"}, {ns: "test.coll1"}]
-}));
+});
+
+assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
assert.eq(coll.find().itcount(), 1);
assert.eq(coll1.find().itcount(), 1);
@@ -57,11 +61,14 @@ coll.drop();
coll1.drop();
// Test 2 inserts into the same namespace
-assert.commandWorked(db.adminCommand({
+res = db.adminCommand({
bulkWrite: 1,
ops: [{insert: 0, document: {skey: "MongoDB"}}, {insert: 0, document: {skey: "MongoDB"}}],
nsInfo: [{ns: "test.coll"}]
-}));
+});
+
+assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
assert.eq(coll.find().itcount(), 2);
assert.eq(coll1.find().itcount(), 0);
@@ -71,12 +78,15 @@ coll.drop();
assert.commandWorked(coll.insert({_id: 1}));
assert.commandWorked(db.runCommand({collMod: "coll", validator: {a: {$exists: true}}}));
-assert.commandWorked(db.adminCommand({
+res = db.adminCommand({
bulkWrite: 1,
ops: [{insert: 0, document: {_id: 3, skey: "MongoDB"}}],
nsInfo: [{ns: "test.coll"}],
bypassDocumentValidation: true,
-}));
+});
+
+assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
assert.eq(1, coll.count({_id: 3}));
diff --git a/jstests/core/write/bulk/bulk_write_delete_cursor.js b/jstests/core/write/bulk/bulk_write_delete_cursor.js
index 4e515cef170..360d18c7841 100644
--- a/jstests/core/write/bulk/bulk_write_delete_cursor.js
+++ b/jstests/core/write/bulk/bulk_write_delete_cursor.js
@@ -38,6 +38,7 @@ var res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, n: 1});
@@ -59,6 +60,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, n: 1});
@@ -81,6 +83,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, n: 1});
@@ -103,6 +106,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, n: 1});
@@ -125,6 +129,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
assert.docEq(res.cursor.firstBatch[0].value, {_id: 1, skey: "MongoDB"});
@@ -146,6 +151,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 0});
assert(!res.cursor.firstBatch[0].value);
@@ -174,6 +180,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, n: 1});
diff --git a/jstests/core/write/bulk/bulk_write_getMore.js b/jstests/core/write/bulk/bulk_write_getMore.js
index b9e6e2f092a..397e2847efa 100644
--- a/jstests/core/write/bulk/bulk_write_getMore.js
+++ b/jstests/core/write/bulk/bulk_write_getMore.js
@@ -49,6 +49,7 @@ var res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
assert(res.cursor.id != 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, n: 1, idx: 0});
diff --git a/jstests/core/write/bulk/bulk_write_insert_cursor.js b/jstests/core/write/bulk/bulk_write_insert_cursor.js
index 9391fc1ffc5..a4168827bb0 100644
--- a/jstests/core/write/bulk/bulk_write_insert_cursor.js
+++ b/jstests/core/write/bulk/bulk_write_insert_cursor.js
@@ -32,6 +32,7 @@ var res = db.adminCommand(
{bulkWrite: 1, ops: [{insert: 0, document: {skey: "MongoDB"}}], nsInfo: [{ns: "test.coll"}]});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
assert(res.cursor.id == 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, n: 1, idx: 0});
@@ -50,6 +51,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
assert(res.cursor.id == 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, n: 1, idx: 0});
diff --git a/jstests/core/write/bulk/bulk_write_non_retryable_cursor.js b/jstests/core/write/bulk/bulk_write_non_retryable_cursor.js
index aca7cfd83d5..89e066a59d7 100644
--- a/jstests/core/write/bulk/bulk_write_non_retryable_cursor.js
+++ b/jstests/core/write/bulk/bulk_write_non_retryable_cursor.js
@@ -47,6 +47,7 @@ var res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, n: 1});
@@ -73,6 +74,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, nModified: 1});
@@ -97,6 +99,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, n: 1});
@@ -119,6 +122,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, n: 1});
diff --git a/jstests/core/write/bulk/bulk_write_non_transaction.js b/jstests/core/write/bulk/bulk_write_non_transaction.js
index 24e83301ef3..967cab5da61 100644
--- a/jstests/core/write/bulk/bulk_write_non_transaction.js
+++ b/jstests/core/write/bulk/bulk_write_non_transaction.js
@@ -114,38 +114,6 @@ assert.commandFailedWithCode(
assert.eq(coll.find().itcount(), 0);
assert.eq(coll1.find().itcount(), 0);
-// Test that a write can fail part way through a write and the write partially executes.
-assert.commandWorked(db.adminCommand({
- bulkWrite: 1,
- ops: [
- {insert: 0, document: {_id: 1, skey: "MongoDB"}},
- {insert: 0, document: {_id: 1, skey: "MongoDB"}},
- {insert: 1, document: {skey: "MongoDB"}}
- ],
- nsInfo: [{ns: "test.coll"}, {ns: "test.coll1"}]
-}));
-
-assert.eq(coll.find().itcount(), 1);
-assert.eq(coll1.find().itcount(), 0);
-coll.drop();
-coll1.drop();
-
-assert.commandWorked(db.adminCommand({
- bulkWrite: 1,
- ops: [
- {insert: 0, document: {_id: 1, skey: "MongoDB"}},
- {insert: 0, document: {_id: 1, skey: "MongoDB"}},
- {insert: 1, document: {skey: "MongoDB"}}
- ],
- nsInfo: [{ns: "test.coll"}, {ns: "test.coll1"}],
- ordered: false
-}));
-
-assert.eq(coll.find().itcount(), 1);
-assert.eq(coll1.find().itcount(), 1);
-coll.drop();
-coll1.drop();
-
// Make sure update multi:true + return fails the op.
var res = db.adminCommand({
bulkWrite: 1,
@@ -162,6 +130,7 @@ var res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 0, idx: 0, code: ErrorCodes.InvalidOptions});
assert(!res.cursor.firstBatch[1]);
@@ -182,6 +151,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 0, idx: 1, code: ErrorCodes.InvalidOptions});
@@ -203,6 +173,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 0, idx: 0, code: ErrorCodes.InvalidNamespace});
assert(!res.cursor.firstBatch[1]);
@@ -230,6 +201,9 @@ res = db.adminCommand({
ordered: false
});
+assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
+
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 0, idx: 0, code: ErrorCodes.DuplicateKey});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, nModified: 0});
assert.docEq(res.cursor.firstBatch[1].upserted, {index: 0, _id: 1});
@@ -258,6 +232,9 @@ res = db.adminCommand({
nsInfo: [{ns: "test.coll2"}, {ns: "test.coll"}],
});
+assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
+
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 0, idx: 0, code: ErrorCodes.DuplicateKey});
assert(!res.cursor.firstBatch[1]);
coll.drop();
@@ -288,6 +265,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
assert(res.cursor.id == 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, n: 1, idx: 0});
@@ -318,6 +296,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
assert(res.cursor.id == 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, n: 1, idx: 0});
@@ -346,6 +325,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
assert(res.cursor.id == 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, n: 1, idx: 0});
@@ -373,6 +353,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
assert(res.cursor.id == 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, n: 1, idx: 0});
@@ -395,6 +376,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 0, idx: 0, code: ErrorCodes.InvalidOptions});
assert(!res.cursor.firstBatch[1]);
@@ -410,6 +392,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 0, idx: 1, code: ErrorCodes.InvalidOptions});
@@ -430,6 +413,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 0, idx: 0, code: ErrorCodes.InvalidNamespace});
assert(!res.cursor.firstBatch[1]);
@@ -449,6 +433,9 @@ res = db.adminCommand({
ordered: false
});
+assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
+
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 0, idx: 0, code: ErrorCodes.InvalidNamespace});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, n: 1});
assert.docEq(res.cursor.firstBatch[1].value, {_id: 1, skey: "MongoDB"});
@@ -473,6 +460,9 @@ res = db.adminCommand({
nsInfo: [{ns: "test.system.profile"}, {ns: "test.coll"}],
});
+assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
+
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 0, idx: 0, code: ErrorCodes.InvalidNamespace});
assert(!res.cursor.firstBatch[1]);
@@ -501,6 +491,7 @@ res = db.adminCommand({
let processCursor = true;
try {
assert.commandWorked(res);
+ assert.eq(res.numErrors, 0);
} catch {
processCursor = false;
assert.commandFailedWithCode(res, [ErrorCodes.BadValue]);
@@ -533,6 +524,7 @@ res = db.adminCommand({
processCursor = true;
try {
assert.commandWorked(res);
+ assert.eq(res.numErrors, 0);
} catch {
processCursor = false;
assert.commandFailedWithCode(res, [ErrorCodes.BadValue]);
@@ -555,12 +547,39 @@ coll.drop();
assert.commandWorked(coll.insert({_id: 1}));
assert.commandWorked(db.runCommand({collMod: "coll", validator: {a: {$exists: true}}}));
-assert.commandWorked(db.adminCommand({
+res = db.adminCommand({
bulkWrite: 1,
ops: [{insert: 0, document: {_id: 3, skey: "MongoDB"}}],
nsInfo: [{ns: "test.coll"}],
bypassDocumentValidation: false,
-}));
+});
+assert.commandWorked(res);
+assert.eq(res.numErrors, 1);
assert.eq(0, coll.count({_id: 3}));
+coll.drop();
+
+// Test that we correctly count multiple errors for different write types when ordered=false.
+res = db.adminCommand({
+ bulkWrite: 1,
+ ops: [
+ {insert: 0, document: {_id: 1}},
+ {insert: 0, document: {_id: 2}},
+ // error 1: duplicate key error
+ {insert: 0, document: {_id: 1}},
+ {delete: 0, filter: {_id: 2}},
+ // error 2: user can't write to namespace
+ {delete: 1, filter: {_id: 0}},
+ {update: 0, filter: {_id: 0}, updateMods: {$set: {x: 1}}},
+ // error 3: invalid update operator
+ {update: 0, filter: {_id: 0}, updateMods: {$blah: {x: 1}}},
+ ],
+ nsInfo: [{ns: "test.coll"}, {ns: "test.system.profile"}],
+ ordered: false
+});
+
+assert.commandWorked(res);
+assert.eq(res.numErrors, 3);
+
+coll.drop();
})();
diff --git a/jstests/core/write/bulk/bulk_write_update_cursor.js b/jstests/core/write/bulk/bulk_write_update_cursor.js
index 03f11014e23..bf4148f4fae 100644
--- a/jstests/core/write/bulk/bulk_write_update_cursor.js
+++ b/jstests/core/write/bulk/bulk_write_update_cursor.js
@@ -39,6 +39,7 @@ var res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, nModified: 1});
@@ -61,6 +62,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, nModified: 1});
@@ -83,6 +85,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, nModified: 1});
@@ -111,6 +114,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, n: 1});
@@ -139,6 +143,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, n: 1});
@@ -161,6 +166,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, nModified: 1});
assert.docEq(res.cursor.firstBatch[0].value, {_id: 1, skey: "MongoDB2"});
@@ -182,6 +188,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, nModified: 0});
assert(!res.cursor.firstBatch[0].value);
@@ -208,6 +215,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, nModified: 0});
assert.docEq(res.cursor.firstBatch[0].upserted, {index: 0, _id: 1});
@@ -234,6 +242,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, nModified: 0});
assert.docEq(res.cursor.firstBatch[0].upserted, {index: 0, _id: 1});
@@ -255,6 +264,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, nModified: 1});
@@ -282,6 +292,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, nModified: 1});
assert.eq(res.cursor.firstBatch[0].nModified, 1);
@@ -309,6 +320,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, n: 1});
@@ -335,6 +347,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, n: 1});
cursorEntryValidator(res.cursor.firstBatch[1], {ok: 1, idx: 1, nModified: 1});
@@ -362,6 +375,7 @@ res = db.adminCommand({
});
assert.commandWorked(res);
+assert.eq(res.numErrors, 0);
cursorEntryValidator(res.cursor.firstBatch[0], {ok: 1, idx: 0, nModified: 0});
assert.docEq(res.cursor.firstBatch[0].upserted, {index: 0, _id: 1});
diff --git a/src/mongo/db/bulk_write_shard_test.cpp b/src/mongo/db/bulk_write_shard_test.cpp
index 77320fd28e9..ce4aef8d85b 100644
--- a/src/mongo/db/bulk_write_shard_test.cpp
+++ b/src/mongo/db/bulk_write_shard_test.cpp
@@ -258,12 +258,14 @@ TEST_F(BulkWriteShardTest, ThreeSuccessfulInsertsOrdered) {
nssShardedCollection2, dbVersionTestDb2, shardVersionShardedCollection2),
});
- const auto& [replyItems, retriedStmtIds] = bulk_write::performWrites(opCtx(), request);
+ const auto& [replyItems, retriedStmtIds, numErrors] =
+ bulk_write::performWrites(opCtx(), request);
ASSERT_EQ(3, replyItems.size());
for (const auto& reply : replyItems) {
ASSERT_OK(reply.getStatus());
}
+ ASSERT_EQ(0, numErrors);
OperationShardingState::get(opCtx()).resetShardingOperationFailedStatus();
}
@@ -278,10 +280,12 @@ TEST_F(BulkWriteShardTest, OneFailingShardedOneSkippedUnshardedSuccessInsertOrde
nsInfoWithShardDatabaseVersions(
nssUnshardedCollection1, dbVersionTestDb1, ShardVersion::UNSHARDED())});
- const auto& [replyItems, retriedStmtIds] = bulk_write::performWrites(opCtx(), request);
+ const auto& [replyItems, retriedStmtIds, numErrors] =
+ bulk_write::performWrites(opCtx(), request);
ASSERT_EQ(1, replyItems.size());
ASSERT_EQ(ErrorCodes::StaleConfig, replyItems.back().getStatus().code());
+ ASSERT_EQ(1, numErrors);
OperationShardingState::get(opCtx()).resetShardingOperationFailedStatus();
}
@@ -296,10 +300,12 @@ TEST_F(BulkWriteShardTest, TwoFailingShardedInsertsOrdered) {
nssShardedCollection1, dbVersionTestDb1, incorrectShardVersion),
});
- const auto& [replyItems, retriedStmtIds] = bulk_write::performWrites(opCtx(), request);
+ const auto& [replyItems, retriedStmtIds, numErrors] =
+ bulk_write::performWrites(opCtx(), request);
ASSERT_EQ(1, replyItems.size());
ASSERT_EQ(ErrorCodes::StaleConfig, replyItems.back().getStatus().code());
+ ASSERT_EQ(1, numErrors);
OperationShardingState::get(opCtx()).resetShardingOperationFailedStatus();
}
@@ -314,11 +320,13 @@ TEST_F(BulkWriteShardTest, OneSuccessfulShardedOneFailingShardedOrdered) {
nsInfoWithShardDatabaseVersions(
nssShardedCollection2, dbVersionTestDb2, incorrectShardVersion)});
- const auto& [replyItems, retriedStmtIds] = bulk_write::performWrites(opCtx(), request);
+ const auto& [replyItems, retriedStmtIds, numErrors] =
+ bulk_write::performWrites(opCtx(), request);
ASSERT_EQ(2, replyItems.size());
ASSERT_OK(replyItems.front().getStatus());
ASSERT_EQ(ErrorCodes::StaleConfig, replyItems.back().getStatus().code());
+ ASSERT_EQ(1, numErrors);
OperationShardingState::get(opCtx()).resetShardingOperationFailedStatus();
}
@@ -332,10 +340,12 @@ TEST_F(BulkWriteShardTest, OneFailingShardedOneSkippedShardedUnordered) {
nssShardedCollection1, dbVersionTestDb1, incorrectShardVersion)});
request.setOrdered(false);
- const auto& [replyItems, retriedStmtIds] = bulk_write::performWrites(opCtx(), request);
+ const auto& [replyItems, retriedStmtIds, numErrors] =
+ bulk_write::performWrites(opCtx(), request);
ASSERT_EQ(1, replyItems.size());
ASSERT_EQ(ErrorCodes::StaleConfig, replyItems.back().getStatus().code());
+ ASSERT_EQ(1, numErrors);
OperationShardingState::get(opCtx()).resetShardingOperationFailedStatus();
}
@@ -351,10 +361,12 @@ TEST_F(BulkWriteShardTest, OneSuccessfulShardedOneFailingShardedUnordered) {
nssShardedCollection2, dbVersionTestDb2, shardVersionShardedCollection2)});
request.setOrdered(false);
- const auto& [replyItems, retriedStmtIds] = bulk_write::performWrites(opCtx(), request);
+ const auto& [replyItems, retriedStmtIds, numErrors] =
+ bulk_write::performWrites(opCtx(), request);
ASSERT_EQ(1, replyItems.size());
ASSERT_EQ(ErrorCodes::StaleConfig, replyItems.back().getStatus().code());
+ ASSERT_EQ(1, numErrors);
OperationShardingState::get(opCtx()).resetShardingOperationFailedStatus();
}
@@ -373,12 +385,14 @@ TEST_F(BulkWriteShardTest, InsertsAndUpdatesSuccessOrdered) {
nsInfoWithShardDatabaseVersions(
nssUnshardedCollection1, dbVersionTestDb1, ShardVersion::UNSHARDED())});
- const auto& [replyItems, retriedStmtIds] = bulk_write::performWrites(opCtx(), request);
+ const auto& [replyItems, retriedStmtIds, numErrors] =
+ bulk_write::performWrites(opCtx(), request);
ASSERT_EQ(4, replyItems.size());
for (const auto& reply : replyItems) {
ASSERT_OK(reply.getStatus());
}
+ ASSERT_EQ(0, numErrors);
OperationShardingState::get(opCtx()).resetShardingOperationFailedStatus();
}
@@ -399,12 +413,14 @@ TEST_F(BulkWriteShardTest, InsertsAndUpdatesSuccessUnordered) {
request.setOrdered(false);
- const auto& [replyItems, retriedStmtIds] = bulk_write::performWrites(opCtx(), request);
+ const auto& [replyItems, retriedStmtIds, numErrors] =
+ bulk_write::performWrites(opCtx(), request);
ASSERT_EQ(4, replyItems.size());
for (const auto& reply : replyItems) {
ASSERT_OK(reply.getStatus());
}
+ ASSERT_EQ(0, numErrors);
OperationShardingState::get(opCtx()).resetShardingOperationFailedStatus();
}
@@ -425,11 +441,13 @@ TEST_F(BulkWriteShardTest, InsertsAndUpdatesFailUnordered) {
request.setOrdered(false);
- const auto& [replyItems, retriedStmtIds] = bulk_write::performWrites(opCtx(), request);
+ const auto& [replyItems, retriedStmtIds, numErrors] =
+ bulk_write::performWrites(opCtx(), request);
ASSERT_EQ(2, replyItems.size());
ASSERT_OK(replyItems.front().getStatus());
ASSERT_EQ(ErrorCodes::StaleConfig, replyItems.back().getStatus().code());
+ ASSERT_EQ(1, numErrors);
OperationShardingState::get(opCtx()).resetShardingOperationFailedStatus();
}
@@ -449,11 +467,13 @@ TEST_F(BulkWriteShardTest, InsertsAndUpdatesFailUnordered) {
// request.setOrdered(false);
-// const auto& [replyItems, retriedStmtIds] = bulk_write::performWrites(opCtx(), request);
+// const auto& [replyItems, retriedStmtIds, numErrors] =
+// bulk_write::performWrites(opCtx(), request);
// ASSERT_EQ(2, replyItems.size());
// ASSERT_OK(replyItems.front().getStatus());
// ASSERT_EQ(ErrorCodes::StaleConfig, replyItems[1].getStatus().code());
+// ASSERT_EQ(1, numErrors);
// OperationShardingState::get(opCtx()).resetShardingOperationFailedStatus();
// }
@@ -470,10 +490,12 @@ TEST_F(BulkWriteShardTest, FirstFailsRestSkippedStaleDbVersionOrdered) {
nsInfoWithShardDatabaseVersions(
nssShardedCollection2, dbVersionTestDb2, shardVersionShardedCollection2)});
- const auto& [replyItems, retriedStmtIds] = bulk_write::performWrites(opCtx(), request);
+ const auto& [replyItems, retriedStmtIds, numErrors] =
+ bulk_write::performWrites(opCtx(), request);
ASSERT_EQ(1, replyItems.size());
ASSERT_EQ(ErrorCodes::StaleDbVersion, replyItems.back().getStatus().code());
+ ASSERT_EQ(1, numErrors);
OperationShardingState::get(opCtx()).resetShardingOperationFailedStatus();
}
@@ -491,11 +513,13 @@ TEST_F(BulkWriteShardTest, FirstFailsRestSkippedStaleDbVersionUnordered) {
nssShardedCollection2, dbVersionTestDb2, shardVersionShardedCollection2)});
request.setOrdered(false);
- const auto& [replyItems, retriedStmtIds] = bulk_write::performWrites(opCtx(), request);
+ const auto& [replyItems, retriedStmtIds, numErrors] =
+ bulk_write::performWrites(opCtx(), request);
ASSERT_EQ(2, replyItems.size());
ASSERT_OK(replyItems.front().getStatus());
ASSERT_EQ(ErrorCodes::StaleDbVersion, replyItems.back().getStatus().code());
+ ASSERT_EQ(1, numErrors);
OperationShardingState::get(opCtx()).resetShardingOperationFailedStatus();
}
diff --git a/src/mongo/db/commands/bulk_write.cpp b/src/mongo/db/commands/bulk_write.cpp
index 6a5ab7b7039..8c0ba323346 100644
--- a/src/mongo/db/commands/bulk_write.cpp
+++ b/src/mongo/db/commands/bulk_write.cpp
@@ -238,6 +238,7 @@ public:
opCtx, writes.results[i].getStatus(), idx, 0 /* numErrors */)) {
auto replyItem = BulkWriteReplyItem(idx, error.get().getStatus());
_replies.emplace_back(replyItem);
+ _numErrors++;
} else {
auto replyItem = BulkWriteReplyItem(idx);
replyItem.setN(writes.results[i].getValue().getN());
@@ -288,6 +289,7 @@ public:
void addErrorReply(size_t currentOpIdx, const Status& status) {
_replies.emplace_back(currentOpIdx, status);
+ _numErrors++;
}
std::vector<BulkWriteReplyItem>& getReplies() {
@@ -298,10 +300,16 @@ public:
return _retriedStmtIds;
}
+ int getNumErrors() {
+ return _numErrors;
+ }
+
private:
const BulkWriteCommandRequest& _req;
std::vector<BulkWriteReplyItem> _replies;
std::vector<int32_t> _retriedStmtIds;
+ /// The number of error replies contained in _replies.
+ int _numErrors = 0;
};
void finishCurOp(OperationContext* opCtx, CurOp* curOp) {
@@ -813,9 +821,10 @@ public:
bulk_write_common::validateRequest(req, opCtx->isRetryableWrite());
// Apply all of the write operations.
- auto [replies, retriedStmtIds] = bulk_write::performWrites(opCtx, req);
+ auto [replies, retriedStmtIds, numErrors] = bulk_write::performWrites(opCtx, req);
- return _populateCursorReply(opCtx, req, std::move(replies), std::move(retriedStmtIds));
+ return _populateCursorReply(
+ opCtx, req, std::move(replies), std::move(retriedStmtIds), numErrors);
}
void doCheckAuthorization(OperationContext* opCtx) const final try {
@@ -835,7 +844,8 @@ public:
Reply _populateCursorReply(OperationContext* opCtx,
const BulkWriteCommandRequest& req,
bulk_write::BulkWriteReplyItems replies,
- bulk_write::RetriedStmtIds retriedStmtIds) {
+ bulk_write::RetriedStmtIds retriedStmtIds,
+ int numErrors) {
auto reqObj = unparsedRequest().body;
const NamespaceString cursorNss = NamespaceString::makeBulkWriteNSS();
auto expCtx = make_intrusive<ExpressionContext>(
@@ -893,8 +903,10 @@ public:
if (exec->isEOF()) {
invariant(numRepliesInFirstBatch == replies.size());
collectTelemetryMongod(opCtx, reqObj, numRepliesInFirstBatch);
- auto reply = BulkWriteCommandReply(BulkWriteCommandResponseCursor(
- 0, std::vector<BulkWriteReplyItem>(std::move(replies))));
+ auto reply = BulkWriteCommandReply(
+ BulkWriteCommandResponseCursor(
+ 0, std::vector<BulkWriteReplyItem>(std::move(replies))),
+ numErrors);
if (!retriedStmtIds.empty()) {
reply.setRetriedStmtIds(std::move(retriedStmtIds));
}
@@ -922,8 +934,10 @@ public:
collectTelemetryMongod(opCtx, pinnedCursor, numRepliesInFirstBatch);
replies.resize(numRepliesInFirstBatch);
- auto reply = BulkWriteCommandReply(BulkWriteCommandResponseCursor(
- cursorId, std::vector<BulkWriteReplyItem>(std::move(replies))));
+ auto reply = BulkWriteCommandReply(
+ BulkWriteCommandResponseCursor(cursorId,
+ std::vector<BulkWriteReplyItem>(std::move(replies))),
+ numErrors);
if (!retriedStmtIds.empty()) {
reply.setRetriedStmtIds(std::move(retriedStmtIds));
}
@@ -993,8 +1007,8 @@ BulkWriteReply performWrites(OperationContext* opCtx, const BulkWriteCommandRequ
}
});
- // Tell mongod what the shard and database versions are. This will cause writes to fail in case
- // there is a mismatch in the mongos request provided versions and the local (shard's)
+ // Tell mongod what the shard and database versions are. This will cause writes to fail in
+ // case there is a mismatch in the mongos request provided versions and the local (shard's)
// understanding of the version.
for (const auto& nsInfo : req.getNsInfo()) {
// TODO (SERVER-72767, SERVER-72804, SERVER-72805): Support timeseries collections.
@@ -1045,7 +1059,8 @@ BulkWriteReply performWrites(OperationContext* opCtx, const BulkWriteCommandRequ
invariant(batch.empty());
- return make_tuple(responses.getReplies(), responses.getRetriedStmtIds());
+ return make_tuple(
+ responses.getReplies(), responses.getRetriedStmtIds(), responses.getNumErrors());
}
} // namespace bulk_write
diff --git a/src/mongo/db/commands/bulk_write.h b/src/mongo/db/commands/bulk_write.h
index ca167f31fea..fa5849b278a 100644
--- a/src/mongo/db/commands/bulk_write.h
+++ b/src/mongo/db/commands/bulk_write.h
@@ -39,7 +39,7 @@ namespace bulk_write {
using RetriedStmtIds = std::vector<int32_t>;
using BulkWriteReplyItems = std::vector<BulkWriteReplyItem>;
-using BulkWriteReply = std::tuple<BulkWriteReplyItems, RetriedStmtIds>;
+using BulkWriteReply = std::tuple<BulkWriteReplyItems, RetriedStmtIds, int /* numErrors */>;
BulkWriteReply performWrites(OperationContext* opCtx, const BulkWriteCommandRequest& req);
diff --git a/src/mongo/db/commands/bulk_write.idl b/src/mongo/db/commands/bulk_write.idl
index f64f1b12697..4deca00cbdb 100644
--- a/src/mongo/db/commands/bulk_write.idl
+++ b/src/mongo/db/commands/bulk_write.idl
@@ -255,6 +255,9 @@ structs:
cursor:
type: BulkWriteCommandResponseCursor
stability: unstable
+ numErrors:
+ type: int
+ stability: unstable
electionId:
type: int
optional: true
diff --git a/src/mongo/s/commands/cluster_bulk_write_cmd.cpp b/src/mongo/s/commands/cluster_bulk_write_cmd.cpp
index 1c62749c8c5..c14cba055f9 100644
--- a/src/mongo/s/commands/cluster_bulk_write_cmd.cpp
+++ b/src/mongo/s/commands/cluster_bulk_write_cmd.cpp
@@ -175,8 +175,10 @@ public:
}
if (numRepliesInFirstBatch == replyItems.size()) {
collectTelemetryMongos(opCtx, reqObj, numRepliesInFirstBatch);
- return BulkWriteCommandReply(BulkWriteCommandResponseCursor(
- 0, std::vector<BulkWriteReplyItem>(std::move(replyItems))));
+ return BulkWriteCommandReply(
+ BulkWriteCommandResponseCursor(
+ 0, std::vector<BulkWriteReplyItem>(std::move(replyItems))),
+ 0 /* TODO SERVER-76267: correctly populate numErrors */);
}
ccc->detachFromOperationContext();
@@ -197,8 +199,10 @@ public:
CurOp::get(opCtx)->debug().cursorid = cursorId;
replyItems.resize(numRepliesInFirstBatch);
- return BulkWriteCommandReply(BulkWriteCommandResponseCursor(
- cursorId, std::vector<BulkWriteReplyItem>(std::move(replyItems))));
+ return BulkWriteCommandReply(
+ BulkWriteCommandResponseCursor(
+ cursorId, std::vector<BulkWriteReplyItem>(std::move(replyItems))),
+ 0 /* TODO SERVER-76267: correctly populate numErrors */);
}
};