/** * apply_ops_wc.js * * This file tests SERVER-22270 that applyOps commands should take a writeConcern. * This first tests that invalid write concerns cause writeConcern errors. * Next, it tests replication with writeConcerns of w:2 and w:majority. * When there are 3 nodes up in a replica set, applyOps commands succeed. * It then stops replication at one seconday and confirms that applyOps commands still succeed. * It finally stops replication at another secondary and confirms that applyOps commands fail. */ load("jstests/libs/write_concern_util.js"); (function() { "use strict"; var nodeCount = 3; var replTest = new ReplSetTest({name: 'applyOpsWCSet', nodes: nodeCount}); replTest.startSet(); var cfg = replTest.getReplSetConfig(); cfg.settings = {}; cfg.settings.chainingAllowed = false; replTest.initiate(cfg); var testDB = "applyOps-wc-test"; // Get test collection. var primary = replTest.getPrimary(); // The default WC is majority and stopServerReplication will prevent satisfying any majority writes. assert.commandWorked(primary.adminCommand( {setDefaultRWConcern: 1, defaultWriteConcern: {w: 1}, writeConcern: {w: "majority"}})); replTest.awaitReplication(); var db = primary.getDB(testDB); var coll = db.apply_ops_wc; function dropTestCollection() { coll.drop(); assert.eq(0, coll.find().itcount(), "test collection not empty"); } dropTestCollection(); // Set up the applyOps command. var applyOpsReq = { applyOps: [ {op: "i", ns: coll.getFullName(), o: {_id: 2, x: "b"}}, {op: "i", ns: coll.getFullName(), o: {_id: 3, x: "c"}}, {op: "i", ns: coll.getFullName(), o: {_id: 4, x: "d"}}, ] }; function assertApplyOpsCommandWorked(res) { assert.eq(3, res.applied); assert.commandWorkedIgnoringWriteConcernErrors(res); assert.eq([true, true, true], res.results); } function assertWriteConcernError(res) { assert(res.writeConcernError); assert(res.writeConcernError.code); assert(res.writeConcernError.errmsg); } var invalidWriteConcerns = [{w: 'invalid'}, {w: nodeCount + 1}]; function testInvalidWriteConcern(wc) { jsTest.log("Testing invalid write concern " + tojson(wc)); applyOpsReq.writeConcern = wc; dropTestCollection(); assert.commandWorked(coll.insert({_id: 1, x: "a"})); var res = coll.runCommand(applyOpsReq); assertApplyOpsCommandWorked(res); assertWriteConcernError(res); } // Verify that invalid write concerns yield an error. coll.insert({_id: 1, x: "a"}); invalidWriteConcerns.forEach(testInvalidWriteConcern); var secondaries = replTest.getSecondaries(); var majorityWriteConcerns = [ {w: 2, wtimeout: 30000}, {w: 'majority', wtimeout: 30000}, ]; function testMajorityWriteConcerns(wc) { jsTest.log("Testing " + tojson(wc)); // Reset secondaries to ensure they can replicate. restartReplicationOnSecondaries(replTest); // Set the writeConcern of the applyOps command. applyOpsReq.writeConcern = wc; dropTestCollection(); // applyOps with a full replica set should succeed. assert.commandWorked(coll.insert({_id: 1, x: "a"})); var res = db.runCommand(applyOpsReq); assertApplyOpsCommandWorked(res); assert(!res.writeConcernError, 'applyOps on a full replicaset had writeConcern error ' + tojson(res.writeConcernError)); dropTestCollection(); // Stop replication at one secondary. stopServerReplication(secondaries[0]); // applyOps should succeed with only 1 node not replicating. assert.commandWorked(coll.insert({_id: 1, x: "a"})); res = db.runCommand(applyOpsReq); assertApplyOpsCommandWorked(res); assert(!res.writeConcernError, 'applyOps on a replicaset with 2 working nodes had writeConcern error ' + tojson(res.writeConcernError)); dropTestCollection(); // Stop replication at a second secondary. stopServerReplication(secondaries[1]); // applyOps should fail after two nodes have stopped replicating. assert.commandWorked(coll.insert({_id: 1, x: "a"})); applyOpsReq.writeConcern.wtimeout = 5000; res = db.runCommand(applyOpsReq); assertApplyOpsCommandWorked(res); assertWriteConcernError(res); } majorityWriteConcerns.forEach(testMajorityWriteConcerns); // Allow clean shutdown restartReplicationOnSecondaries(replTest); replTest.stopSet(); })();