'use strict'; /** * update_upsert_multi.js * * Tests updates that specify both multi=true and upsert=true. * The 'insert' state uses a query that will match no documents, causing an upsert. * The 'update' state uses a query that will match one or more documents, causing a multi-update. * Both states use { multi: true, upsert: true }, but only one option will ever take effect, * depending on whether 0 or more than 0 documents match the query. * * @tags: [requires_non_retryable_writes] */ var $config = (function() { var states = { insert: function insert(db, collName) { var query, update, options; var res = db[collName].update( // The counter ensures that the query will not match any existing document. query = {tid: this.tid, i: this.counter++}, update = {$inc: {n: 1}}, options = {multi: true, upsert: true}); var debugDoc = tojson({query: query, update: update, options: options, result: res}); assertWhenOwnColl.eq(1, res.nUpserted, debugDoc); assertWhenOwnColl.eq(0, res.nMatched, debugDoc); if (db.getMongo().writeMode() === 'commands') { assertWhenOwnColl.eq(0, res.nModified, debugDoc); } }, update: function update(db, collName) { var res = db[collName].update( // This query will match an existing document, since the 'insert' state // always runs first. {tid: this.tid}, {$inc: {n: 1}}, {multi: true, upsert: true}); assertWhenOwnColl.eq(0, res.nUpserted, tojson(res)); assertWhenOwnColl.lte(1, res.nMatched, tojson(res)); if (db.getMongo().writeMode() === 'commands') { assertWhenOwnColl.eq(res.nMatched, res.nModified, tojson(res)); } }, assertConsistency: function assertConsistency(db, collName) { // Since each update operation either: // - inserts a new doc { tid: tid, i: counter++, n: 0 } // - updates any doc matching { tid: tid } with { $inc: { n: 1 } } // Then within each tid, as you walk from lower to higher values of i, // the value of n should be non-increasing. (This should be true // because docs with lower i are newer, so they have had fewer // opportunities to have n incremented.) var prevN = Infinity; db[collName].find({tid: this.tid}).sort({i: 1}).forEach(function(doc) { assertWhenOwnColl.gte(prevN, doc.n); prevN = doc.n; }); } }; var transitions = { insert: {update: 0.875, assertConsistency: 0.125}, update: {insert: 0.875, assertConsistency: 0.125}, assertConsistency: {insert: 0.5, update: 0.5} }; function setup(db, collName, cluster) { assertAlways.commandWorked(db[collName].ensureIndex({tid: 1, i: 1})); } return { threadCount: 10, iterations: 20, states: states, startState: 'insert', transitions: transitions, // Shard by tid when run in a sharded cluster because upserts require the shard key. data: {counter: 0, shardKey: {tid: 1}}, setup: setup }; })();