2017-03-23 14:07:50 -04:00
|
|
|
// Cannot implicitly shard accessed collections because of following errmsg: A single
|
|
|
|
|
// update/delete on a sharded collection must contain an exact match on _id or contain the shard
|
|
|
|
|
// key.
|
2018-01-30 19:45:42 -05:00
|
|
|
// @tags: [
|
|
|
|
|
// assumes_unsharded_collection,
|
|
|
|
|
// assumes_write_concern_unchanged,
|
|
|
|
|
// requires_non_retryable_writes,
|
2018-04-06 10:33:03 -04:00
|
|
|
// requires_fastcount,
|
2018-01-30 19:45:42 -05:00
|
|
|
// ]
|
2017-03-23 14:07:50 -04:00
|
|
|
|
2013-11-11 19:15:22 -05:00
|
|
|
//
|
|
|
|
|
// Ensures that mongod respects the batch write protocols for updates
|
|
|
|
|
//
|
|
|
|
|
|
2016-03-09 12:17:50 -05:00
|
|
|
var coll = db.getCollection("batch_write_update");
|
2013-11-12 17:27:15 -05:00
|
|
|
coll.drop();
|
2013-11-11 19:15:22 -05:00
|
|
|
|
2016-02-04 12:29:01 -05:00
|
|
|
assert(coll.getDB().getMongo().useWriteCommands(), "test is not running with write commands");
|
2014-02-26 11:25:50 -05:00
|
|
|
|
2013-11-11 19:15:22 -05:00
|
|
|
var request;
|
|
|
|
|
var result;
|
2014-03-13 00:46:39 -04:00
|
|
|
var batch;
|
|
|
|
|
|
2017-07-25 16:20:22 -04:00
|
|
|
var maxWriteBatchSize = db.isMaster().maxWriteBatchSize;
|
2013-11-11 19:15:22 -05:00
|
|
|
|
2016-03-09 12:17:50 -05:00
|
|
|
function resultOK(result) {
|
|
|
|
|
return result.ok && !('code' in result) && !('errmsg' in result) && !('errInfo' in result) &&
|
|
|
|
|
!('writeErrors' in result);
|
2016-02-04 12:29:01 -05:00
|
|
|
}
|
2013-11-11 19:15:22 -05:00
|
|
|
|
2016-03-09 12:17:50 -05:00
|
|
|
function resultNOK(result) {
|
2019-07-26 18:20:35 -04:00
|
|
|
return !result.ok && typeof (result.code) == 'number' && typeof (result.errmsg) == 'string';
|
2016-02-04 12:29:01 -05:00
|
|
|
}
|
2013-11-11 19:15:22 -05:00
|
|
|
|
2018-12-12 11:16:13 -05:00
|
|
|
function countEventually(collection, n) {
|
|
|
|
|
assert.soon(
|
|
|
|
|
function() {
|
|
|
|
|
return collection.count() === n;
|
|
|
|
|
},
|
|
|
|
|
function() {
|
|
|
|
|
return "unacknowledged write timed out";
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-11 19:15:22 -05:00
|
|
|
// EACH TEST BELOW SHOULD BE SELF-CONTAINED, FOR EASIER DEBUGGING
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// NO DOCS, illegal command
|
|
|
|
|
coll.remove({});
|
2016-03-09 12:17:50 -05:00
|
|
|
request = {
|
|
|
|
|
update: coll.getName()
|
|
|
|
|
};
|
2014-10-13 17:04:56 -04:00
|
|
|
result = coll.runCommand(request);
|
|
|
|
|
assert(resultNOK(result), tojson(result));
|
2013-11-11 19:15:22 -05:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Single document upsert, no write concern specified
|
|
|
|
|
coll.remove({});
|
2016-03-09 12:17:50 -05:00
|
|
|
request = {
|
|
|
|
|
update: coll.getName(),
|
|
|
|
|
updates: [{q: {a: 1}, u: {$set: {a: 1}}, upsert: true}]
|
|
|
|
|
};
|
2014-10-13 17:04:56 -04:00
|
|
|
result = coll.runCommand(request);
|
|
|
|
|
assert(resultOK(result), tojson(result));
|
2013-11-11 19:15:22 -05:00
|
|
|
assert.eq(1, result.n);
|
2016-03-09 12:17:50 -05:00
|
|
|
assert('upserted' in result);
|
2013-12-19 13:10:42 -05:00
|
|
|
assert.eq(1, result.upserted.length);
|
|
|
|
|
assert.eq(0, result.upserted[0].index);
|
|
|
|
|
|
2013-11-11 19:15:22 -05:00
|
|
|
// Count the upserted doc
|
2013-12-19 13:10:42 -05:00
|
|
|
var upsertedId = result.upserted[0]._id;
|
|
|
|
|
assert.eq(1, coll.count({_id: upsertedId}));
|
2016-02-04 12:29:01 -05:00
|
|
|
assert.eq(0, result.nModified, "missing/wrong nModified");
|
2013-11-11 19:15:22 -05:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Single document upsert, write concern specified, no ordered specified
|
|
|
|
|
coll.remove({});
|
2016-03-09 12:17:50 -05:00
|
|
|
request = {
|
|
|
|
|
update: coll.getName(),
|
|
|
|
|
updates: [{q: {a: 1}, u: {$set: {a: 1}}, upsert: true}],
|
|
|
|
|
writeConcern: {w: 1}
|
|
|
|
|
};
|
2014-10-13 17:04:56 -04:00
|
|
|
result = coll.runCommand(request);
|
|
|
|
|
assert(resultOK(result), tojson(result));
|
2013-11-11 19:15:22 -05:00
|
|
|
assert.eq(1, result.n);
|
2016-03-09 12:17:50 -05:00
|
|
|
assert('upserted' in result);
|
2013-12-19 13:10:42 -05:00
|
|
|
assert.eq(1, result.upserted.length);
|
|
|
|
|
assert.eq(0, result.upserted[0].index);
|
|
|
|
|
|
2013-11-11 19:15:22 -05:00
|
|
|
// Count the upserted doc
|
2013-12-19 13:10:42 -05:00
|
|
|
upsertedId = result.upserted[0]._id;
|
|
|
|
|
assert.eq(1, coll.count({_id: upsertedId}));
|
2016-02-04 12:29:01 -05:00
|
|
|
assert.eq(0, result.nModified, "missing/wrong nModified");
|
2013-11-11 19:15:22 -05:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Single document upsert, write concern specified, ordered = true
|
|
|
|
|
coll.remove({});
|
2016-03-09 12:17:50 -05:00
|
|
|
request = {
|
|
|
|
|
update: coll.getName(),
|
|
|
|
|
updates: [{q: {a: 1}, u: {$set: {a: 1}}, upsert: true}],
|
|
|
|
|
writeConcern: {w: 1},
|
|
|
|
|
ordered: true
|
|
|
|
|
};
|
2014-10-13 17:04:56 -04:00
|
|
|
result = coll.runCommand(request);
|
|
|
|
|
assert(resultOK(result), tojson(result));
|
2013-11-11 19:15:22 -05:00
|
|
|
assert.eq(1, result.n);
|
2016-03-09 12:17:50 -05:00
|
|
|
assert('upserted' in result);
|
2013-12-19 13:10:42 -05:00
|
|
|
assert.eq(1, result.upserted.length);
|
|
|
|
|
assert.eq(0, result.upserted[0].index);
|
|
|
|
|
|
2013-11-11 19:15:22 -05:00
|
|
|
// Count the upserted doc
|
2013-12-19 13:10:42 -05:00
|
|
|
upsertedId = result.upserted[0]._id;
|
|
|
|
|
assert.eq(1, coll.count({_id: upsertedId}));
|
2016-02-04 12:29:01 -05:00
|
|
|
assert.eq(0, result.nModified, "missing/wrong nModified");
|
2013-11-11 19:15:22 -05:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Single document update
|
|
|
|
|
coll.remove({});
|
2016-03-09 12:17:50 -05:00
|
|
|
coll.insert({a: 1});
|
|
|
|
|
request = {
|
|
|
|
|
update: coll.getName(),
|
|
|
|
|
updates: [{q: {a: 1}, u: {$set: {c: 1}}}],
|
|
|
|
|
writeConcern: {w: 1}
|
|
|
|
|
};
|
2014-10-13 17:04:56 -04:00
|
|
|
result = coll.runCommand(request);
|
|
|
|
|
assert(resultOK(result), tojson(result));
|
2013-11-11 19:15:22 -05:00
|
|
|
assert.eq(1, result.n);
|
|
|
|
|
assert(!('upserted' in result));
|
|
|
|
|
assert.eq(1, coll.count());
|
2014-10-13 17:04:56 -04:00
|
|
|
assert.eq(1, result.nModified, "missing/wrong nModified");
|
2013-11-11 19:15:22 -05:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Multi document update/upsert
|
|
|
|
|
coll.remove({});
|
2016-03-09 12:17:50 -05:00
|
|
|
coll.insert({b: 1});
|
|
|
|
|
request = {
|
|
|
|
|
update: coll.getName(),
|
|
|
|
|
updates: [
|
|
|
|
|
{q: {b: 1}, u: {$set: {b: 1, a: 1}}, upsert: true},
|
|
|
|
|
{q: {b: 2}, u: {$set: {b: 2, a: 1}}, upsert: true}
|
|
|
|
|
],
|
|
|
|
|
writeConcern: {w: 1},
|
|
|
|
|
ordered: false
|
|
|
|
|
};
|
2014-10-13 17:04:56 -04:00
|
|
|
result = coll.runCommand(request);
|
|
|
|
|
assert(resultOK(result), tojson(result));
|
2013-11-11 19:15:22 -05:00
|
|
|
assert.eq(2, result.n);
|
2014-10-13 17:04:56 -04:00
|
|
|
assert.eq(1, result.nModified, "missing/wrong nModified");
|
2013-11-11 19:15:22 -05:00
|
|
|
|
|
|
|
|
assert.eq(1, result.upserted.length);
|
|
|
|
|
assert.eq(1, result.upserted[0].index);
|
|
|
|
|
assert.eq(1, coll.count({_id: result.upserted[0]._id}));
|
|
|
|
|
assert.eq(2, coll.count());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Multiple document update
|
|
|
|
|
coll.remove({});
|
2016-03-09 12:17:50 -05:00
|
|
|
coll.insert({a: 1});
|
|
|
|
|
coll.insert({a: 1});
|
|
|
|
|
request = {
|
|
|
|
|
update: coll.getName(),
|
|
|
|
|
updates: [{q: {a: 1}, u: {$set: {c: 2}}, multi: true}],
|
|
|
|
|
writeConcern: {w: 1},
|
|
|
|
|
ordered: false
|
|
|
|
|
};
|
2014-10-13 17:04:56 -04:00
|
|
|
result = coll.runCommand(request);
|
|
|
|
|
assert(resultOK(result), tojson(result));
|
2013-11-11 19:15:22 -05:00
|
|
|
assert.eq(2, result.n);
|
2014-10-13 17:04:56 -04:00
|
|
|
assert.eq(2, result.nModified, "missing/wrong nModified");
|
2016-03-09 12:17:50 -05:00
|
|
|
assert.eq(2, coll.find({a: 1, c: 2}).count());
|
2013-11-11 19:15:22 -05:00
|
|
|
assert.eq(2, coll.count());
|
|
|
|
|
|
2013-12-13 08:44:24 -05:00
|
|
|
//
|
2016-03-09 12:17:50 -05:00
|
|
|
// Multiple document update, some no-ops
|
2013-12-13 08:44:24 -05:00
|
|
|
coll.remove({});
|
2016-03-09 12:17:50 -05:00
|
|
|
coll.insert({a: 1, c: 2});
|
|
|
|
|
coll.insert({a: 1});
|
|
|
|
|
request = {
|
|
|
|
|
update: coll.getName(),
|
|
|
|
|
updates: [{q: {a: 1}, u: {$set: {c: 2}}, multi: true}],
|
|
|
|
|
writeConcern: {w: 1},
|
|
|
|
|
ordered: false
|
|
|
|
|
};
|
|
|
|
|
printjson(result = coll.runCommand(request));
|
2014-10-13 17:04:56 -04:00
|
|
|
assert(resultOK(result), tojson(result));
|
2013-12-13 08:44:24 -05:00
|
|
|
assert.eq(2, result.n);
|
2014-10-13 17:04:56 -04:00
|
|
|
assert.eq(1, result.nModified, "missing/wrong nModified");
|
2016-03-09 12:17:50 -05:00
|
|
|
assert.eq(2, coll.find({a: 1, c: 2}).count());
|
2013-12-13 08:44:24 -05:00
|
|
|
assert.eq(2, coll.count());
|
|
|
|
|
|
2014-03-13 00:46:39 -04:00
|
|
|
//
|
|
|
|
|
// Large batch under the size threshold should update successfully
|
|
|
|
|
coll.remove({});
|
2016-03-09 12:17:50 -05:00
|
|
|
coll.insert({a: 0});
|
2014-03-13 00:46:39 -04:00
|
|
|
batch = [];
|
|
|
|
|
for (var i = 0; i < maxWriteBatchSize; ++i) {
|
2016-03-09 12:17:50 -05:00
|
|
|
batch.push({q: {}, u: {$inc: {a: 1}}});
|
2014-03-13 00:46:39 -04:00
|
|
|
}
|
2016-03-09 12:17:50 -05:00
|
|
|
request = {
|
|
|
|
|
update: coll.getName(),
|
|
|
|
|
updates: batch,
|
|
|
|
|
writeConcern: {w: 1},
|
|
|
|
|
ordered: false
|
|
|
|
|
};
|
2014-10-13 17:04:56 -04:00
|
|
|
result = coll.runCommand(request);
|
|
|
|
|
assert(resultOK(result), tojson(result));
|
2014-03-13 00:46:39 -04:00
|
|
|
assert.eq(batch.length, result.n);
|
2014-10-13 17:04:56 -04:00
|
|
|
assert.eq(batch.length, result.nModified, "missing/wrong nModified");
|
2016-03-09 12:17:50 -05:00
|
|
|
assert.eq(1, coll.find({a: batch.length}).count());
|
2014-03-13 00:46:39 -04:00
|
|
|
assert.eq(1, coll.count());
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Large batch above the size threshold should fail to update
|
|
|
|
|
coll.remove({});
|
2016-03-09 12:17:50 -05:00
|
|
|
coll.insert({a: 0});
|
2014-03-13 00:46:39 -04:00
|
|
|
batch = [];
|
|
|
|
|
for (var i = 0; i < maxWriteBatchSize + 1; ++i) {
|
2016-03-09 12:17:50 -05:00
|
|
|
batch.push({q: {}, u: {$inc: {a: 1}}});
|
2014-03-13 00:46:39 -04:00
|
|
|
}
|
2016-03-09 12:17:50 -05:00
|
|
|
request = {
|
|
|
|
|
update: coll.getName(),
|
|
|
|
|
updates: batch,
|
|
|
|
|
writeConcern: {w: 1},
|
|
|
|
|
ordered: false
|
|
|
|
|
};
|
2014-10-13 17:04:56 -04:00
|
|
|
result = coll.runCommand(request);
|
|
|
|
|
assert(resultNOK(result), tojson(result));
|
2016-03-09 12:17:50 -05:00
|
|
|
assert.eq(1, coll.find({a: 0}).count());
|
2014-03-13 00:46:39 -04:00
|
|
|
assert.eq(1, coll.count());
|
|
|
|
|
|
2013-11-11 19:15:22 -05:00
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// Unique index tests
|
|
|
|
|
coll.remove({});
|
2016-03-09 12:17:50 -05:00
|
|
|
coll.ensureIndex({a: 1}, {unique: true});
|
2013-11-11 19:15:22 -05:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Upsert fail due to duplicate key index, w:1, ordered:true
|
|
|
|
|
coll.remove({});
|
2016-03-09 12:17:50 -05:00
|
|
|
request = {
|
|
|
|
|
update: coll.getName(),
|
|
|
|
|
updates: [
|
|
|
|
|
{q: {b: 1}, u: {$set: {b: 1, a: 1}}, upsert: true},
|
|
|
|
|
{q: {b: 3}, u: {$set: {b: 3, a: 2}}, upsert: true},
|
|
|
|
|
{q: {b: 2}, u: {$set: {b: 2, a: 1}}, upsert: true}
|
|
|
|
|
],
|
|
|
|
|
writeConcern: {w: 1},
|
|
|
|
|
ordered: true
|
|
|
|
|
};
|
2014-10-13 17:04:56 -04:00
|
|
|
result = coll.runCommand(request);
|
|
|
|
|
assert(result.ok, tojson(result));
|
2013-11-11 19:15:22 -05:00
|
|
|
assert.eq(2, result.n);
|
2014-10-13 17:04:56 -04:00
|
|
|
assert.eq(0, result.nModified, "wrong nModified");
|
2013-12-16 17:04:25 -05:00
|
|
|
assert.eq(1, result.writeErrors.length);
|
2013-11-11 19:15:22 -05:00
|
|
|
|
2013-12-16 17:04:25 -05:00
|
|
|
assert.eq(2, result.writeErrors[0].index);
|
|
|
|
|
assert.eq('number', typeof result.writeErrors[0].code);
|
|
|
|
|
assert.eq('string', typeof result.writeErrors[0].errmsg);
|
2013-11-11 19:15:22 -05:00
|
|
|
|
|
|
|
|
assert.eq(2, result.upserted.length);
|
|
|
|
|
assert.eq(0, result.upserted[0].index);
|
|
|
|
|
assert.eq(1, coll.count({_id: result.upserted[0]._id}));
|
|
|
|
|
|
|
|
|
|
assert.eq(1, result.upserted[1].index);
|
|
|
|
|
assert.eq(1, coll.count({_id: result.upserted[1]._id}));
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Upsert fail due to duplicate key index, w:1, ordered:false
|
|
|
|
|
coll.remove({});
|
2016-03-09 12:17:50 -05:00
|
|
|
request = {
|
|
|
|
|
update: coll.getName(),
|
|
|
|
|
updates: [
|
|
|
|
|
{q: {b: 1}, u: {$set: {b: 1, a: 1}}, upsert: true},
|
|
|
|
|
{q: {b: 2}, u: {$set: {b: 2, a: 1}}, upsert: true},
|
|
|
|
|
{q: {b: 2}, u: {$set: {b: 2, a: 1}}, upsert: true},
|
|
|
|
|
{q: {b: 3}, u: {$set: {b: 3, a: 3}}, upsert: true}
|
|
|
|
|
],
|
|
|
|
|
writeConcern: {w: 1},
|
|
|
|
|
ordered: false
|
|
|
|
|
};
|
2014-10-13 17:04:56 -04:00
|
|
|
result = coll.runCommand(request);
|
|
|
|
|
assert(result.ok, tojson(result));
|
2013-11-11 19:15:22 -05:00
|
|
|
assert.eq(2, result.n);
|
2014-03-17 12:13:25 -04:00
|
|
|
assert.eq(0, result.nModified, "wrong nModified");
|
2013-12-16 17:04:25 -05:00
|
|
|
assert.eq(2, result.writeErrors.length);
|
2013-11-11 19:15:22 -05:00
|
|
|
|
2013-12-16 17:04:25 -05:00
|
|
|
assert.eq(1, result.writeErrors[0].index);
|
|
|
|
|
assert.eq('number', typeof result.writeErrors[0].code);
|
|
|
|
|
assert.eq('string', typeof result.writeErrors[0].errmsg);
|
2013-11-11 19:15:22 -05:00
|
|
|
|
2013-12-16 17:04:25 -05:00
|
|
|
assert.eq(2, result.writeErrors[1].index);
|
|
|
|
|
assert.eq('number', typeof result.writeErrors[1].code);
|
|
|
|
|
assert.eq('string', typeof result.writeErrors[1].errmsg);
|
2013-11-11 19:15:22 -05:00
|
|
|
|
|
|
|
|
assert.eq(2, result.upserted.length);
|
|
|
|
|
assert.eq(0, result.upserted[0].index);
|
|
|
|
|
assert.eq(1, coll.count({_id: result.upserted[0]._id}));
|
|
|
|
|
|
|
|
|
|
assert.eq(3, result.upserted[1].index);
|
|
|
|
|
assert.eq(1, coll.count({_id: result.upserted[1]._id}));
|