summaryrefslogtreecommitdiff
path: root/jstests/replsets/apply_ops_idempotency.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/replsets/apply_ops_idempotency.js')
-rw-r--r--jstests/replsets/apply_ops_idempotency.js374
1 files changed, 187 insertions, 187 deletions
diff --git a/jstests/replsets/apply_ops_idempotency.js b/jstests/replsets/apply_ops_idempotency.js
index 7173bbd0301..1a3167bdbde 100644
--- a/jstests/replsets/apply_ops_idempotency.js
+++ b/jstests/replsets/apply_ops_idempotency.js
@@ -1,199 +1,199 @@
(function() {
- 'use strict';
- const debug = 0;
-
- let rst = new ReplSetTest({name: "applyOpsIdempotency", nodes: 1});
- rst.startSet();
- rst.initiate();
-
- /**
- * Returns true if this database contains any drop-pending collections.
- */
- function containsDropPendingCollection(mydb) {
- const res =
- assert.commandWorked(mydb.runCommand("listCollections", {includePendingDrops: true}));
- const collectionInfos = res.cursor.firstBatch;
- const collectionNames = collectionInfos.map(c => c.name);
- return Boolean(collectionNames.find(c => c.indexOf('system.drop.') == 0));
- }
-
- /**
- * Apply ops on mydb, asserting success.
- */
- function assertApplyOpsWorks(testdbs, ops) {
- // Remaining operations in ops must still be applied
- while (ops.length) {
- let cmd = {applyOps: ops};
- let res = testdbs[0].adminCommand(cmd);
- if (debug) {
- printjson({applyOps: ops, res});
- }
-
- // Wait for any drop-pending collections to be removed by the reaper before proceeding.
- assert.soon(function() {
- return !testdbs.find(mydb => containsDropPendingCollection(mydb));
- });
-
- // If the entire operation succeeded, we're done.
- if (res.ok == 1)
- return res;
-
- // Skip any operations that succeeded.
- while (res.applied-- && res.results.shift())
- ops.shift();
-
- // These errors are expected when replaying operations and should be ignored.
- if (res.code == ErrorCodes.NamespaceNotFound || res.code == ErrorCodes.DuplicateKey) {
- ops.shift();
- continue;
- }
-
- // Generate the appropriate error message.
- assert.commandWorked(res, tojson(cmd));
+'use strict';
+const debug = 0;
+
+let rst = new ReplSetTest({name: "applyOpsIdempotency", nodes: 1});
+rst.startSet();
+rst.initiate();
+
+/**
+ * Returns true if this database contains any drop-pending collections.
+ */
+function containsDropPendingCollection(mydb) {
+ const res =
+ assert.commandWorked(mydb.runCommand("listCollections", {includePendingDrops: true}));
+ const collectionInfos = res.cursor.firstBatch;
+ const collectionNames = collectionInfos.map(c => c.name);
+ return Boolean(collectionNames.find(c => c.indexOf('system.drop.') == 0));
+}
+
+/**
+ * Apply ops on mydb, asserting success.
+ */
+function assertApplyOpsWorks(testdbs, ops) {
+ // Remaining operations in ops must still be applied
+ while (ops.length) {
+ let cmd = {applyOps: ops};
+ let res = testdbs[0].adminCommand(cmd);
+ if (debug) {
+ printjson({applyOps: ops, res});
}
- }
- /**
- * Run the dbHash command on mydb, assert it worked and return the md5.
- */
- function dbHash(mydb) {
- let cmd = {dbHash: 1};
- let res = mydb.runCommand(cmd);
- assert.commandWorked(res, tojson(cmd));
- return res.md5;
- }
-
- /**
- * Gather collection info and dbHash results of each of the passed databases.
- */
- function dbInfo(dbs) {
- return dbs.map((db) => {
- return {name: db.getName(), info: db.getCollectionInfos(), md5: dbHash(db)};
+ // Wait for any drop-pending collections to be removed by the reaper before proceeding.
+ assert.soon(function() {
+ return !testdbs.find(mydb => containsDropPendingCollection(mydb));
});
- }
- var getCollections = (mydb, prefixes) => prefixes.map((prefix) => mydb[prefix]);
-
- /**
- * Test functions to run and test using replay of oplog.
- */
- var tests = {
- crud: (mydb) => {
- let [x, y, z] = getCollections(mydb, ['x', 'y', 'z']);
- assert.writeOK(x.insert({_id: 1}));
- assert.writeOK(x.update({_id: 1}, {$set: {x: 1}}));
- assert.writeOK(x.remove({_id: 1}));
-
- assert.writeOK(y.update({_id: 1}, {y: 1}));
- assert.writeOK(y.insert({_id: 2, y: false, z: false}));
- assert.writeOK(y.update({_id: 2}, {y: 2}));
-
- assert.writeOK(z.insert({_id: 1, z: 1}));
- assert.writeOK(z.remove({_id: 1}));
- assert.writeOK(z.insert({_id: 1}));
- assert.writeOK(z.insert({_id: 2, z: 2}));
- },
- renameCollectionWithinDatabase: (mydb) => {
- let [x, y, z] = getCollections(mydb, ['x', 'y', 'z']);
- assert.writeOK(x.insert({_id: 1, x: 1}));
- assert.writeOK(y.insert({_id: 1, y: 1}));
-
- assert.commandWorked(x.renameCollection(z.getName()));
- assert.writeOK(z.insert({_id: 2, x: 2}));
- assert.writeOK(x.insert({_id: 2, x: false}));
- assert.writeOK(y.insert({y: 2}));
-
- assert.commandWorked(y.renameCollection(x.getName(), true));
- assert.commandWorked(z.renameCollection(y.getName()));
- },
- renameCollectionWithinDatabaseDroppingTargetByUUID: (mydb) => {
- assert.commandWorked(mydb.createCollection("x"));
- assert.commandWorked(mydb.createCollection("y"));
- assert.commandWorked(mydb.createCollection("z"));
-
- assert.commandWorked(mydb.x.renameCollection('xx'));
- // When replayed on a up-to-date db, this oplog entry may drop
- // collection z rather than collection x if the dropTarget is not
- // specified by UUID. (See SERVER-33087)
- assert.commandWorked(mydb.y.renameCollection('xx', true));
- assert.commandWorked(mydb.xx.renameCollection('yy'));
- assert.commandWorked(mydb.z.renameCollection('xx'));
- },
- renameCollectionWithinDatabaseDropTargetEvenWhenSourceIsEmpty: (mydb) => {
- assert.commandWorked(mydb.createCollection("x"));
- assert.commandWorked(mydb.createCollection("y"));
- assert.commandWorked(mydb.x.renameCollection('y', true));
- assert(mydb.y.drop());
- },
- renameCollectionAcrossDatabases: (mydb) => {
- let otherdb = mydb.getSiblingDB(mydb + '_');
- let [x, y] = getCollections(mydb, ['x', 'y']);
- let [z] = getCollections(otherdb, ['z']);
- assert.writeOK(x.insert({_id: 1, x: 1}));
- assert.writeOK(y.insert({_id: 1, y: 1}));
-
- assert.commandWorked(
- mydb.adminCommand({renameCollection: x.getFullName(), to: z.getFullName()}));
- assert.writeOK(z.insert({_id: 2, x: 2}));
- assert.writeOK(x.insert({_id: 2, x: false}));
- assert.writeOK(y.insert({y: 2}));
-
- assert.commandWorked(mydb.adminCommand(
- {renameCollection: y.getFullName(), to: x.getFullName(), dropTarget: true}));
- assert.commandWorked(
- mydb.adminCommand({renameCollection: z.getFullName(), to: y.getFullName()}));
- return [mydb, otherdb];
- },
- createIndex: (mydb) => {
- let [x, y] = getCollections(mydb, ['x', 'y']);
- assert.commandWorked(x.createIndex({x: 1}));
- assert.writeOK(x.insert({_id: 1, x: 1}));
- assert.writeOK(y.insert({_id: 1, y: 1}));
- assert.commandWorked(y.createIndex({y: 1}));
- assert.writeOK(y.insert({_id: 2, y: 2}));
- },
- };
-
- /**
- * Create a new uniquely named database, execute testFun and compute the dbHash. Then replay
- * all different suffixes of the oplog and check for the correct hash. If testFun creates
- * additional databases, it should return an array with all databases to check.
- */
- function testIdempotency(primary, testFun, testName) {
- // Create a new database name, so it's easier to filter out our oplog records later.
- let dbname = (new Date()).toISOString().match(/[-0-9T]/g).join(''); // 2017-05-30T155055713
- let mydb = primary.getDB(dbname);
-
- // Allow testFun to return the array of databases to check (default is mydb).
- let testdbs = testFun(mydb) || [mydb];
- let expectedInfo = dbInfo(testdbs);
-
- let oplog = mydb.getSiblingDB('local').oplog.rs;
- let ops = oplog
- .find({op: {$ne: 'n'}, ns: new RegExp('^' + mydb.getName())},
- {ts: 0, t: 0, h: 0, v: 0})
- .toArray();
- assert.gt(ops.length, 0, 'Could not find any matching ops in the oplog');
- testdbs.forEach((db) => assert.commandWorked(db.dropDatabase()));
+ // If the entire operation succeeded, we're done.
+ if (res.ok == 1)
+ return res;
- if (debug) {
- print(testName + ': replaying suffixes of ' + ops.length + ' operations');
- printjson(ops);
- }
+ // Skip any operations that succeeded.
+ while (res.applied-- && res.results.shift())
+ ops.shift();
- for (let j = 0; j < ops.length; j++) {
- let replayOps = ops.slice(j);
- assertApplyOpsWorks(testdbs, replayOps);
- let actualInfo = dbInfo(testdbs);
- assert.eq(actualInfo,
- expectedInfo,
- 'unexpected differences between databases after replaying final ' +
- replayOps.length + ' ops in test ' + testName + ": " + tojson(replayOps));
+ // These errors are expected when replaying operations and should be ignored.
+ if (res.code == ErrorCodes.NamespaceNotFound || res.code == ErrorCodes.DuplicateKey) {
+ ops.shift();
+ continue;
}
+
+ // Generate the appropriate error message.
+ assert.commandWorked(res, tojson(cmd));
+ }
+}
+
+/**
+ * Run the dbHash command on mydb, assert it worked and return the md5.
+ */
+function dbHash(mydb) {
+ let cmd = {dbHash: 1};
+ let res = mydb.runCommand(cmd);
+ assert.commandWorked(res, tojson(cmd));
+ return res.md5;
+}
+
+/**
+ * Gather collection info and dbHash results of each of the passed databases.
+ */
+function dbInfo(dbs) {
+ return dbs.map((db) => {
+ return {name: db.getName(), info: db.getCollectionInfos(), md5: dbHash(db)};
+ });
+}
+
+var getCollections = (mydb, prefixes) => prefixes.map((prefix) => mydb[prefix]);
+
+/**
+ * Test functions to run and test using replay of oplog.
+ */
+var tests = {
+ crud: (mydb) => {
+ let [x, y, z] = getCollections(mydb, ['x', 'y', 'z']);
+ assert.writeOK(x.insert({_id: 1}));
+ assert.writeOK(x.update({_id: 1}, {$set: {x: 1}}));
+ assert.writeOK(x.remove({_id: 1}));
+
+ assert.writeOK(y.update({_id: 1}, {y: 1}));
+ assert.writeOK(y.insert({_id: 2, y: false, z: false}));
+ assert.writeOK(y.update({_id: 2}, {y: 2}));
+
+ assert.writeOK(z.insert({_id: 1, z: 1}));
+ assert.writeOK(z.remove({_id: 1}));
+ assert.writeOK(z.insert({_id: 1}));
+ assert.writeOK(z.insert({_id: 2, z: 2}));
+ },
+ renameCollectionWithinDatabase: (mydb) => {
+ let [x, y, z] = getCollections(mydb, ['x', 'y', 'z']);
+ assert.writeOK(x.insert({_id: 1, x: 1}));
+ assert.writeOK(y.insert({_id: 1, y: 1}));
+
+ assert.commandWorked(x.renameCollection(z.getName()));
+ assert.writeOK(z.insert({_id: 2, x: 2}));
+ assert.writeOK(x.insert({_id: 2, x: false}));
+ assert.writeOK(y.insert({y: 2}));
+
+ assert.commandWorked(y.renameCollection(x.getName(), true));
+ assert.commandWorked(z.renameCollection(y.getName()));
+ },
+ renameCollectionWithinDatabaseDroppingTargetByUUID: (mydb) => {
+ assert.commandWorked(mydb.createCollection("x"));
+ assert.commandWorked(mydb.createCollection("y"));
+ assert.commandWorked(mydb.createCollection("z"));
+
+ assert.commandWorked(mydb.x.renameCollection('xx'));
+ // When replayed on a up-to-date db, this oplog entry may drop
+ // collection z rather than collection x if the dropTarget is not
+ // specified by UUID. (See SERVER-33087)
+ assert.commandWorked(mydb.y.renameCollection('xx', true));
+ assert.commandWorked(mydb.xx.renameCollection('yy'));
+ assert.commandWorked(mydb.z.renameCollection('xx'));
+ },
+ renameCollectionWithinDatabaseDropTargetEvenWhenSourceIsEmpty: (mydb) => {
+ assert.commandWorked(mydb.createCollection("x"));
+ assert.commandWorked(mydb.createCollection("y"));
+ assert.commandWorked(mydb.x.renameCollection('y', true));
+ assert(mydb.y.drop());
+ },
+ renameCollectionAcrossDatabases: (mydb) => {
+ let otherdb = mydb.getSiblingDB(mydb + '_');
+ let [x, y] = getCollections(mydb, ['x', 'y']);
+ let [z] = getCollections(otherdb, ['z']);
+ assert.writeOK(x.insert({_id: 1, x: 1}));
+ assert.writeOK(y.insert({_id: 1, y: 1}));
+
+ assert.commandWorked(
+ mydb.adminCommand({renameCollection: x.getFullName(), to: z.getFullName()}));
+ assert.writeOK(z.insert({_id: 2, x: 2}));
+ assert.writeOK(x.insert({_id: 2, x: false}));
+ assert.writeOK(y.insert({y: 2}));
+
+ assert.commandWorked(mydb.adminCommand(
+ {renameCollection: y.getFullName(), to: x.getFullName(), dropTarget: true}));
+ assert.commandWorked(
+ mydb.adminCommand({renameCollection: z.getFullName(), to: y.getFullName()}));
+ return [mydb, otherdb];
+ },
+ createIndex: (mydb) => {
+ let [x, y] = getCollections(mydb, ['x', 'y']);
+ assert.commandWorked(x.createIndex({x: 1}));
+ assert.writeOK(x.insert({_id: 1, x: 1}));
+ assert.writeOK(y.insert({_id: 1, y: 1}));
+ assert.commandWorked(y.createIndex({y: 1}));
+ assert.writeOK(y.insert({_id: 2, y: 2}));
+ },
+};
+
+/**
+ * Create a new uniquely named database, execute testFun and compute the dbHash. Then replay
+ * all different suffixes of the oplog and check for the correct hash. If testFun creates
+ * additional databases, it should return an array with all databases to check.
+ */
+function testIdempotency(primary, testFun, testName) {
+ // Create a new database name, so it's easier to filter out our oplog records later.
+ let dbname = (new Date()).toISOString().match(/[-0-9T]/g).join(''); // 2017-05-30T155055713
+ let mydb = primary.getDB(dbname);
+
+ // Allow testFun to return the array of databases to check (default is mydb).
+ let testdbs = testFun(mydb) || [mydb];
+ let expectedInfo = dbInfo(testdbs);
+
+ let oplog = mydb.getSiblingDB('local').oplog.rs;
+ let ops =
+ oplog
+ .find({op: {$ne: 'n'}, ns: new RegExp('^' + mydb.getName())}, {ts: 0, t: 0, h: 0, v: 0})
+ .toArray();
+ assert.gt(ops.length, 0, 'Could not find any matching ops in the oplog');
+ testdbs.forEach((db) => assert.commandWorked(db.dropDatabase()));
+
+ if (debug) {
+ print(testName + ': replaying suffixes of ' + ops.length + ' operations');
+ printjson(ops);
+ }
+
+ for (let j = 0; j < ops.length; j++) {
+ let replayOps = ops.slice(j);
+ assertApplyOpsWorks(testdbs, replayOps);
+ let actualInfo = dbInfo(testdbs);
+ assert.eq(actualInfo,
+ expectedInfo,
+ 'unexpected differences between databases after replaying final ' +
+ replayOps.length + ' ops in test ' + testName + ": " + tojson(replayOps));
}
+}
- for (let f in tests)
- testIdempotency(rst.getPrimary(), tests[f], f);
+for (let f in tests)
+ testIdempotency(rst.getPrimary(), tests[f], f);
- rst.stopSet();
+rst.stopSet();
})();