summaryrefslogtreecommitdiff
path: root/jstests/replsets/standalone_replication_recovery_idempotent.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/replsets/standalone_replication_recovery_idempotent.js')
-rw-r--r--jstests/replsets/standalone_replication_recovery_idempotent.js164
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