diff options
author | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2017-09-20 11:03:05 -0400 |
---|---|---|
committer | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2017-09-20 11:15:47 -0400 |
commit | c565d15a53cd6dd452da97d49c6b9c6cbffb6cf1 (patch) | |
tree | 93abf8b71235cceb895df4ce50dda2ef14929948 /jstests/replsets | |
parent | 8a5656c58c18e7c23d5bec6f9b2d41013ee80a47 (diff) | |
download | mongo-c565d15a53cd6dd452da97d49c6b9c6cbffb6cf1.tar.gz |
SERVER-31114 Perform targeted session invalidation on direct writes to `config.transactions`
Diffstat (limited to 'jstests/replsets')
-rw-r--r-- | jstests/replsets/retryable_writes_direct_write_to_config_transactions.js | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/jstests/replsets/retryable_writes_direct_write_to_config_transactions.js b/jstests/replsets/retryable_writes_direct_write_to_config_transactions.js new file mode 100644 index 00000000000..62d0b840711 --- /dev/null +++ b/jstests/replsets/retryable_writes_direct_write_to_config_transactions.js @@ -0,0 +1,83 @@ +// Validates the expected behaviour of direct writes against the `config.transactions` collection +(function() { + 'use strict'; + + var replTest = new ReplSetTest({nodes: 2}); + replTest.startSet(); + replTest.initiate(); + + var priConn = replTest.getPrimary(); + var db = priConn.getDB('TestDB'); + var config = priConn.getDB('config'); + + assert.writeOK(db.user.insert({_id: 0})); + assert.writeOK(db.user.insert({_id: 1})); + + const lsid1 = UUID(); + const lsid2 = UUID(); + + const cmdObj1 = { + update: 'user', + updates: [{q: {_id: 0}, u: {$inc: {x: 1}}}], + lsid: {id: lsid1}, + txnNumber: NumberLong(1) + }; + assert.commandWorked(db.runCommand(cmdObj1)); + assert.eq(1, db.user.find({_id: 0}).toArray()[0].x); + + const cmdObj2 = { + update: 'user', + updates: [{q: {_id: 1}, u: {$inc: {x: 1}}}], + lsid: {id: lsid2}, + txnNumber: NumberLong(1) + }; + assert.commandWorked(db.runCommand(cmdObj2)); + assert.eq(1, db.user.find({_id: 1}).toArray()[0].x); + + assert.eq(1, config.transactions.find({'_id.id': lsid1}).itcount()); + assert.eq(1, config.transactions.find({'_id.id': lsid2}).itcount()); + + // Invalidating lsid1 doesn't impact lsid2, but allows same statement to be executed again + assert.writeOK(config.transactions.remove({'_id.id': lsid1})); + assert.commandWorked(db.runCommand(cmdObj1)); + assert.eq(2, db.user.find({_id: 0}).toArray()[0].x); + assert.commandWorked(db.runCommand(cmdObj2)); + assert.eq(1, db.user.find({_id: 1}).toArray()[0].x); + + // Ensure lsid1 is properly tracked after the recreate + assert.commandWorked(db.runCommand(cmdObj1)); + assert.eq(2, db.user.find({_id: 0}).toArray()[0].x); + + // Ensure garbage data cannot be written to the `config.transactions` collection + assert.writeError(config.transactions.insert({_id: 'String'})); + assert.writeError(config.transactions.insert({_id: {UnknownField: 'Garbage'}})); + + // Ensure inserting an invalid session record manually without all the required fields causes + // the session to not work anymore for retryable writes for that session, but not for any other + const lsidManual = config.transactions.find({'_id.id': lsid1}).toArray()[0]._id; + assert.writeOK(config.transactions.remove({'_id.id': lsid1})); + assert.writeOK(config.transactions.insert({_id: lsidManual})); + + const lsid3 = UUID(); + assert.commandWorked(db.runCommand({ + update: 'user', + updates: [{q: {_id: 2}, u: {$inc: {x: 1}}, upsert: true}], + lsid: {id: lsid3}, + txnNumber: NumberLong(1) + })); + assert.eq(1, db.user.find({_id: 2}).toArray()[0].x); + + // Ensure dropping the `config.transactions` collection breaks the retryable writes feature, but + // doesn't crash the server + assert(config.transactions.drop()); + var res = assert.commandWorked(db.runCommand(cmdObj2)); + assert.eq(0, res.nModified); + assert.eq(1, db.user.find({_id: 1}).toArray()[0].x); + + assert(config.dropDatabase()); + res = assert.commandWorked(db.runCommand(cmdObj2)); + assert.eq(0, res.nModified); + assert.eq(1, db.user.find({_id: 1}).toArray()[0].x); + + replTest.stopSet(); +})(); |