/** * 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. */ (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 master = replTest.getPrimary(); var db = master.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. secondaries[0].getDB('admin').runCommand({configureFailPoint: 'rsSyncApplyStop', mode: 'off'}); secondaries[1].getDB('admin').runCommand({configureFailPoint: 'rsSyncApplyStop', mode: 'off'}); // 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. secondaries[0].getDB('admin').runCommand( {configureFailPoint: 'rsSyncApplyStop', mode: 'alwaysOn'}); // 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. secondaries[1].getDB('admin').runCommand( {configureFailPoint: 'rsSyncApplyStop', mode: 'alwaysOn'}); // 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 secondaries[0].getDB('admin').runCommand({configureFailPoint: 'rsSyncApplyStop', mode: 'off'}); secondaries[1].getDB('admin').runCommand({configureFailPoint: 'rsSyncApplyStop', mode: 'off'}); replTest.stopSet(); })();