diff options
author | Ali Mir <ali.mir@mongodb.com> | 2020-05-14 14:25:09 -0400 |
---|---|---|
committer | Ali Mir <ali.mir@mongodb.com> | 2020-05-29 00:06:40 -0400 |
commit | 7434646ab0c2f94c4ae1cdefc1b5c3764964eb30 (patch) | |
tree | 4f1f4bc529023333ec87cd2bb7d04805a334a19c /jstests/replsets/prepare_transaction_read_at_cluster_time.js | |
parent | e44e6b4f3c0b7fbd604a452ac8afc59dda3f54fa (diff) | |
download | mongo-7434646ab0c2f94c4ae1cdefc1b5c3764964eb30.tar.gz |
SERVER-44782 Support atClusterTime in dbHash commandSERVER-47782
Diffstat (limited to 'jstests/replsets/prepare_transaction_read_at_cluster_time.js')
-rw-r--r-- | jstests/replsets/prepare_transaction_read_at_cluster_time.js | 168 |
1 files changed, 123 insertions, 45 deletions
diff --git a/jstests/replsets/prepare_transaction_read_at_cluster_time.js b/jstests/replsets/prepare_transaction_read_at_cluster_time.js index 40f59c90e76..eb3eb2922a7 100644 --- a/jstests/replsets/prepare_transaction_read_at_cluster_time.js +++ b/jstests/replsets/prepare_transaction_read_at_cluster_time.js @@ -1,8 +1,8 @@ /** * Ensures that performing a write in a prepared transaction, followed by a write outside of a - * transaction, it is possible to specify '$_internalReadAtClusterTime' as the timestamp of - * the second write for 'find' and 'dbHash'. The commands should block until the prepared - * transaction is committed or aborted. + * transaction, it is possible to specify either '$_internalReadAtClusterTime' or snapshot read + * concern with 'atClusterTime' as the timestamp of the second write for 'find' and 'dbHash'. The + * commands should block until the prepared transaction is committed or aborted. * * @tags: [uses_transactions, uses_prepare_transaction] */ @@ -12,16 +12,19 @@ load("jstests/core/txns/libs/prepare_helpers.js"); load("jstests/libs/parallelTester.js"); -const runDBHashFn = (host, dbName, clusterTime) => { +const runDBHashFn = (host, dbName, cmd) => { const conn = new Mongo(host); const db = conn.getDB(dbName); conn.setSlaveOk(); - let firstHash = assert.commandWorked(db.runCommand({ - dbHash: 1, - $_internalReadAtClusterTime: eval(clusterTime), - })); - + // When passing the cmd object through a ScopedThread constructor, + // the Timestamp value does not serialize correctly. In order to correct this behavior + // and provide the correct serialization of Timestamp, we rehydrate using eval(). + cmd.hasOwnProperty('$_internalReadAtClusterTime') + ? cmd.$_internalReadAtClusterTime = eval(cmd.$_internalReadAtClusterTime) + : cmd.readConcern.atClusterTime = eval(cmd.readConcern.atClusterTime); + + let firstHash = assert.commandWorked(db.runCommand(cmd)); // This code will execute once the prepared transaction is committed as the call above will // be blocked until an abort or commit happens. Ensure that running dbHash here yields the // same result as above. @@ -33,29 +36,27 @@ const runDBHashFn = (host, dbName, clusterTime) => { return firstHash; }; -const runFindFn = (host, dbName, collName, clusterTime) => { +const runFindFn = (host, dbName, cmd, clusterTime) => { const conn = new Mongo(host); const db = conn.getDB(dbName); conn.setSlaveOk(); - assert.commandWorked(db.getSiblingDB(dbName).runCommand({ - find: collName, - $_internalReadAtClusterTime: eval(clusterTime), - })); + // When passing the cmd object through a ScopedThread constructor, + // the Timestamp value does not serialize correctly. In order to correct this behavior + // and provide the correct serialization of Timestamp, we rehydrate using eval(). + cmd.hasOwnProperty('$_internalReadAtClusterTime') + ? cmd.$_internalReadAtClusterTime = eval(clusterTime) + : cmd.readConcern.atClusterTime = eval(clusterTime); + assert.commandWorked(db.getSiblingDB(dbName).runCommand(cmd)); }; -const assertOpHasPrepareConflict = (db, commandName) => { +const assertOpHasPrepareConflict = (db, opsObj) => { assert.soon( () => { - const ops = db.currentOp({ - "command.$_internalReadAtClusterTime": {$exists: true}, - ["command." + commandName]: {$exists: true}, - }).inprog; - + const ops = db.currentOp(opsObj).inprog; if (ops.length === 1) { return ops[0].prepareReadConflicts > 0; } - return false; }, () => `Failed to find '${commandName}' command in the ${db.getMongo().host} currentOp()` + @@ -103,34 +104,99 @@ const prepareTimestamp = PrepareHelpers.prepareTransaction(session); // to the secondary because we're going to read from it at the returned operationTime. assert.commandWorked(testDB.getCollection(collName).insert({x: 2}, {writeConcern: {w: 2}})); -// It should be possible to specify '$_internalReadAtClusterTime' as the timestamp of the +// It should be possible to specify either '$_internalReadAtClusterTime' or snapshot read +// concern with 'atClusterTime' as the timestamp of the // second write without an error for dbHash and find. let clusterTime = testDB.getSession().getOperationTime(); // Run dbHash and find while the prepared transaction has not commit or aborted yet. // These should block until the prepared transaction commits or aborts if we specify -// $_internalReadAtClusterTime to be the timestamp of the second write we did, outside of the -// transaction. -const dbHashPrimaryThread = new Thread(runDBHashFn, primary.host, dbName, tojson(clusterTime)); -const dbHashSecondaryThread = new Thread(runDBHashFn, secondary.host, dbName, tojson(clusterTime)); +// $_internalReadAtClusterTime or snapshot read concern with 'atClusterTime' to be the timestamp of +// the second write we did, outside of the transaction. +let cmd = {dbHash: 1, $_internalReadAtClusterTime: tojson(clusterTime)} + +const dbHashInternalClusterTimePrimaryThread = new Thread(runDBHashFn, primary.host, dbName, cmd); +const dbHashInternalClusterTimeSecondaryThread = + new Thread(runDBHashFn, secondary.host, dbName, cmd); + +dbHashInternalClusterTimePrimaryThread.start(); +dbHashInternalClusterTimeSecondaryThread.start(); -dbHashPrimaryThread.start(); -dbHashSecondaryThread.start(); +let curOpObj = { + "command.$_internalReadAtClusterTime": {$exists: true}, + "command.dbHash": {$exists: true}, +} -assertOpHasPrepareConflict(testDB, "dbHash"); -assertOpHasPrepareConflict(testDBSecondary, "dbHash"); +assertOpHasPrepareConflict(testDB, curOpObj); +assertOpHasPrepareConflict(testDBSecondary, curOpObj); -// Run 'find' with '$_internalReadAtClusterTime' specified. -const findPrimaryThread = - new Thread(runFindFn, primary.host, dbName, collName, tojson(clusterTime)); -const findSecondaryThread = - new Thread(runFindFn, secondary.host, dbName, collName, tojson(clusterTime)); +cmd = { + dbHash: 1, + readConcern: {level: "snapshot", atClusterTime: tojson(clusterTime)} +} -findPrimaryThread.start(); -findSecondaryThread.start(); +const dbHashClusterTimePrimaryThread = + new Thread(runDBHashFn, primary.host, dbName, cmd, tojson(clusterTime)); +const dbHashClusterTimeSecondaryThread = + new Thread(runDBHashFn, secondary.host, dbName, cmd, tojson(clusterTime)); -assertOpHasPrepareConflict(testDB, "find"); -assertOpHasPrepareConflict(testDBSecondary, "find"); +dbHashClusterTimePrimaryThread.start(); +dbHashClusterTimeSecondaryThread.start(); + +curOpObj = { + "command.readConcern.atClusterTime": {$exists: true}, + "command.dbHash": {$exists: true}, +} + +assertOpHasPrepareConflict(testDB, curOpObj); +assertOpHasPrepareConflict(testDBSecondary, curOpObj); + +// Run 'find' with '$_internalReadAtClusterTime' and snapshot read concern specified. + +cmd = { + find: collName, + $_internalReadAtClusterTime: eval(clusterTime), +}; + +const findInternalClusterTimePrimaryThread = + new Thread(runFindFn, primary.host, dbName, cmd, tojson(clusterTime)); +const findInternalClusterTimeSecondaryThread = + new Thread(runFindFn, secondary.host, dbName, cmd, tojson(clusterTime)); + +findInternalClusterTimePrimaryThread.start(); +findInternalClusterTimeSecondaryThread.start(); + +curOpObj = { + "command.$_internalReadAtClusterTime": {$exists: true}, + "command.find": {$exists: true}, +}; + +assertOpHasPrepareConflict(testDB, curOpObj); +assertOpHasPrepareConflict(testDBSecondary, curOpObj); + +cmd = { + find: collName, + readConcern: { + level: "snapshot", + atClusterTime: eval(clusterTime), + } +}; + +const findClusterTimePrimaryThread = + new Thread(runFindFn, primary.host, dbName, cmd, tojson(clusterTime)); +const findClusterTimeSecondaryThread = + new Thread(runFindFn, secondary.host, dbName, cmd, tojson(clusterTime)); + +findClusterTimePrimaryThread.start(); +findClusterTimeSecondaryThread.start(); + +curOpObj = { + "command.readConcern.atClusterTime": {$exists: true}, + "command.find": {$exists: true}, +}; + +assertOpHasPrepareConflict(testDB, curOpObj); +assertOpHasPrepareConflict(testDBSecondary, curOpObj); // Run a series of DDL operations which shouldn't block before committing the prepared // transaction. @@ -149,18 +215,30 @@ assert.commandWorked( PrepareHelpers.commitTransaction(session, prepareTimestamp); session.endSession(); -dbHashPrimaryThread.join(); -dbHashSecondaryThread.join(); +dbHashInternalClusterTimePrimaryThread.join(); +dbHashInternalClusterTimeSecondaryThread.join(); + +dbHashClusterTimePrimaryThread.join(); +dbHashClusterTimeSecondaryThread.join(); // Ensure the dbHashes across the replica set match. -const primaryDBHash = dbHashPrimaryThread.returnData(); -const secondaryDBHash = dbHashSecondaryThread.returnData(); +let primaryDBHash = dbHashInternalClusterTimePrimaryThread.returnData(); +let secondaryDBHash = dbHashInternalClusterTimeSecondaryThread.returnData(); assert.eq(primaryDBHash.collections, secondaryDBHash.collections); assert.eq(primaryDBHash.md5, secondaryDBHash.md5); -findPrimaryThread.join(); -findSecondaryThread.join(); +primaryDBHash = dbHashClusterTimePrimaryThread.returnData(); +secondaryDBHash = dbHashClusterTimeSecondaryThread.returnData(); + +assert.eq(primaryDBHash.collections, secondaryDBHash.collections); +assert.eq(primaryDBHash.md5, secondaryDBHash.md5); + +findInternalClusterTimePrimaryThread.join(); +findInternalClusterTimeSecondaryThread.join(); + +findClusterTimePrimaryThread.join(); +findClusterTimeSecondaryThread.join(); rst.stopSet(); }()); |