diff options
Diffstat (limited to 'jstests/replsets/standalone_replication_recovery_idempotent.js')
-rw-r--r-- | jstests/replsets/standalone_replication_recovery_idempotent.js | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/jstests/replsets/standalone_replication_recovery_idempotent.js b/jstests/replsets/standalone_replication_recovery_idempotent.js new file mode 100644 index 00000000000..9108ba2e2e9 --- /dev/null +++ b/jstests/replsets/standalone_replication_recovery_idempotent.js @@ -0,0 +1,164 @@ +/* + * Tests that a 'recoverFromOplogAsStandalone' with 'takeUnstableCheckpointOnShutdown' is + * idempotent. + * + * This test only makes sense for storage engines that support recover to stable timestamp. + * @tags: [requires_wiredtiger, requires_persistence, requires_journaling, requires_replication, + * requires_majority_read_concern, uses_transactions, uses_prepare_transaction] + */ + +(function() { +"use strict"; +load("jstests/replsets/rslib.js"); +load("jstests/libs/write_concern_util.js"); +load("jstests/core/txns/libs/prepare_helpers.js"); + +const name = jsTestName(); +const dbName = name; +const collName1 = 'srri_coll1'; +const collName2 = 'srri_coll2'; + +const logLevel = tojson({storage: {recovery: 2}}); + +const rst = new ReplSetTest({ + nodes: 2, +}); + +function getColl1(conn) { + return conn.getDB(dbName)[collName1]; +} + +function getColl2(conn) { + return conn.getDB(dbName)[collName2]; +} + +function assertDocsInColl1(node, nums) { + let results = getColl1(node).find().sort({_id: 1}).toArray(); + let expected = nums.map((i) => ({_id: i})); + if (!friendlyEqual(results, expected)) { + rst.dumpOplog(node, {}, 100); + } + assert.eq(results, expected, "actual (left) != expected (right)"); +} + +function assertPrepareConflictColl2(node, id) { + assert.sameMembers(getColl2(node).find().toArray(), [{_id: id}]); + assert.commandFailedWithCode( + node.getDB(dbName).runCommand( + {update: collName2, updates: [{q: {_id: id}, u: {$inc: {x: 1}}}], maxTimeMS: 1000}), + ErrorCodes.MaxTimeMSExpired); +} + +jsTestLog("Initiating as a replica set."); +let nodes = rst.startSet({setParameter: {logComponentVerbosity: logLevel}}); +let node = nodes[0]; +let secondary = nodes[1]; +rst.initiate( + {_id: name, members: [{_id: 0, host: node.host}, {_id: 1, host: secondary.host, priority: 0}]}); + +// Create two collections with w:majority and then perform a clean restart to ensure that +// the collections are in a stable checkpoint. +assert.commandWorked(getColl1(node).insert({_id: 3}, {writeConcern: {w: "majority"}})); +assert.commandWorked(getColl2(node).insert({_id: 1}, {writeConcern: {w: "majority"}})); + +node = rst.restart(node, {"noReplSet": false}); +reconnect(node); +assert.eq(rst.getPrimary(), node); +assertDocsInColl1(node, [3]); +assert.sameMembers(getColl2(node).find().toArray(), [{_id: 1}]); + +// Keep node 0 the primary, but prevent it from committing any writes. +stopServerReplication(secondary); +assert.commandWorked(getColl1(node).insert({_id: 4}, {writeConcern: {w: 1, j: 1}})); +assertDocsInColl1(node, [3, 4]); + +let session = node.startSession(); +const sessionDB = session.getDatabase(dbName); +const sessionColl2 = sessionDB.getCollection(collName2); +session.startTransaction(); +const txnNumber = session.getTxnNumber_forTesting(); +assert.commandWorked(sessionColl2.update({_id: 1}, {_id: 1, a: 1})); +let prepareTimestamp = PrepareHelpers.prepareTransaction(session, {w: 1, j: 1}); +jsTestLog("Prepared a transaction at " + prepareTimestamp); +assertPrepareConflictColl2(node, 1); + +jsTestLog("Test that on restart with just 'recoverFromOplogAsStandalone' set we play recovery."); +node = rst.restart(node, { + noReplSet: true, + setParameter: {recoverFromOplogAsStandalone: true, logComponentVerbosity: logLevel} +}); +reconnect(node); +assertDocsInColl1(node, [3, 4]); +assertPrepareConflictColl2(node, 1); +assert.commandFailedWithCode(getColl1(node).insert({_id: 7}), ErrorCodes.IllegalOperation); + +jsTestLog("Test that on restart with just 'recoverFromOplogAsStandalone' we succeed" + + " idempotently."); +node = rst.restart(node, { + noReplSet: true, + setParameter: {recoverFromOplogAsStandalone: true, logComponentVerbosity: logLevel} +}); +reconnect(node); +assertDocsInColl1(node, [3, 4]); +assertPrepareConflictColl2(node, 1); +assert.commandFailedWithCode(getColl1(node).insert({_id: 7}), ErrorCodes.IllegalOperation); + +jsTestLog("Test that on restart with both flags we succeed."); +node = rst.restart(node, { + noReplSet: true, + setParameter: { + recoverFromOplogAsStandalone: true, + takeUnstableCheckpointOnShutdown: true, + logComponentVerbosity: logLevel + } +}); +reconnect(node); +assertDocsInColl1(node, [3, 4]); +assertPrepareConflictColl2(node, 1); +assert.commandFailedWithCode(getColl1(node).insert({_id: 7}), ErrorCodes.IllegalOperation); + +jsTestLog("Test that on restart with both flags we succeed idempotently."); +node = rst.restart(node, { + noReplSet: true, + setParameter: { + recoverFromOplogAsStandalone: true, + takeUnstableCheckpointOnShutdown: true, + logComponentVerbosity: logLevel + } +}); +reconnect(node); +assertDocsInColl1(node, [3, 4]); +assertPrepareConflictColl2(node, 1); +assert.commandFailedWithCode(getColl1(node).insert({_id: 7}), ErrorCodes.IllegalOperation); + +jsTestLog("Restart as a replica set node so that we can commit the transaction"); +node = rst.restart(node, { + noReplSet: false, + setParameter: { + recoverFromOplogAsStandalone: false, + takeUnstableCheckpointOnShutdown: false, + logComponentVerbosity: logLevel + } +}); +reconnect(node); +assert.eq(rst.getPrimary(), node); +assertDocsInColl1(node, [3, 4]); +assertPrepareConflictColl2(node, 1); + +restartServerReplication(secondary); +rst.awaitReplication(); + +assertDocsInColl1(node, [3, 4]); +assertPrepareConflictColl2(node, 1); + +assert.commandWorked(node.adminCommand({ + commitTransaction: 1, + commitTimestamp: prepareTimestamp, + lsid: session.getSessionId(), + txnNumber: NumberLong(txnNumber), + autocommit: false +})); +assert.sameMembers(getColl2(node).find().toArray(), [{_id: 1, a: 1}]); + +rst.stopSet(); +})();
\ No newline at end of file |