diff options
Diffstat (limited to 'jstests/multiVersion/internal_sessions_downgrade.js')
-rw-r--r-- | jstests/multiVersion/internal_sessions_downgrade.js | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/jstests/multiVersion/internal_sessions_downgrade.js b/jstests/multiVersion/internal_sessions_downgrade.js new file mode 100644 index 00000000000..7b342b9b667 --- /dev/null +++ b/jstests/multiVersion/internal_sessions_downgrade.js @@ -0,0 +1,283 @@ +/* + * Test that internal sessions documents are properly removed from the config.transactions + * collection. + * + * @tags: [requires_fcv_51, featureFlagInternalTransactions] + */ +(function() { +'use strict'; + +const kConfigTxnNs = "config.transactions"; +const kDbName = "testDb"; +const kCollName = "testColl"; + +(() => { + jsTest.log( + "Test downgrade updates existing parent session document with highest txnNumber of its " + + "child sessions"); + + const st = new ShardingTest({shards: {rs0: {nodes: 2}}}); + const shard0Rst = st.rs0; + const shard0Primary = shard0Rst.getPrimary(); + + const testDB = shard0Primary.getDB(kDbName); + + const sessionUUID = UUID(); + const parentLsid = {id: sessionUUID}; + const childLsid0 = {id: sessionUUID, txnNumber: NumberLong(5), txnUUID: UUID()}; + const childLsid1 = {id: sessionUUID, txnNumber: NumberLong(6), txnUUID: UUID()}; + + // Create the parent and child sessions + assert.commandWorked(testDB.runCommand( + {insert: kCollName, documents: [{x: 0}], lsid: parentLsid, txnNumber: NumberLong(4)})); + + assert.commandWorked(testDB.runCommand({ + insert: kCollName, + documents: [{x: 1}], + lsid: childLsid0, + txnNumber: NumberLong(0), + startTransaction: true, + autocommit: false + })); + + assert.commandWorked(testDB.adminCommand( + {commitTransaction: 1, lsid: childLsid0, txnNumber: NumberLong(0), autocommit: false})); + + assert.commandWorked(testDB.runCommand({ + insert: kCollName, + documents: [{x: 2}], + lsid: childLsid1, + txnNumber: NumberLong(0), + startTransaction: true, + autocommit: false + })); + + assert.commandWorked(testDB.adminCommand( + {commitTransaction: 1, lsid: childLsid1, txnNumber: NumberLong(0), autocommit: false})); + + const parentDocBeforeDowngrade = + shard0Primary.getCollection(kConfigTxnNs).findOne({"_id.id": sessionUUID}); + + assert.commandWorked(shard0Primary.adminCommand({setFeatureCompatibilityVersion: lastLTSFCV})); + + shard0Rst.nodes.forEach(node => { + // We expect every document except for the parent session documents to be deleted. + const collectionDocCount = node.getCollection(kConfigTxnNs).count(); + assert.eq(collectionDocCount, 1); + + const doc = node.getCollection(kConfigTxnNs).findOne({"_id.id": sessionUUID}); + assert.eq(doc.txnNum, NumberLong(6)); + assert.eq(doc.lastWriteOpTime.ts, Timestamp(1, 0)); + assert.eq(doc.lastWriteOpTime.t, NumberLong(1)); + assert.gte(doc.lastWriteDate, parentDocBeforeDowngrade.lastWriteDate); + }); + + st.stop(); +})(); + +(() => { + jsTest.log( + "Test downgrade upserts new transaction document if relevant parent session document " + + "does not exist and does not modify unrelated transaction documents."); + + const st = new ShardingTest({shards: {rs0: {nodes: 2}}}); + const shard0Rst = st.rs0; + const shard0Primary = shard0Rst.getPrimary(); + + const kDbName = "testDb"; + const kCollName = "testColl"; + const testDB = shard0Primary.getDB(kDbName); + + const sessionUUID = UUID(); + const unrelatedParentLsid = {id: UUID()}; + const childLsid0 = {id: sessionUUID, txnNumber: NumberLong(7), txnUUID: UUID()}; + const childLsid1 = {id: sessionUUID, txnNumber: NumberLong(8), txnUUID: UUID()}; + + // Start a parent session without related children. + assert.commandWorked(testDB.runCommand({ + insert: kCollName, + documents: [{x: 0}], + lsid: unrelatedParentLsid, + txnNumber: NumberLong(10) + })); + + assert.commandWorked(testDB.runCommand({ + insert: kCollName, + documents: [{x: 1}], + lsid: childLsid0, + txnNumber: NumberLong(0), + startTransaction: true, + autocommit: false + })); + + assert.commandWorked(testDB.adminCommand( + {commitTransaction: 1, lsid: childLsid0, txnNumber: NumberLong(0), autocommit: false})); + + assert.commandWorked(testDB.runCommand({ + insert: kCollName, + documents: [{x: 2}], + lsid: childLsid1, + txnNumber: NumberLong(0), + startTransaction: true, + autocommit: false + })); + + assert.commandWorked(testDB.adminCommand( + {commitTransaction: 1, lsid: childLsid1, txnNumber: NumberLong(0), autocommit: false})); + + const unrelatedParentDocBeforeDowngrade = + shard0Primary.getCollection(kConfigTxnNs).findOne({"_id.id": unrelatedParentLsid.id}); + + assert.commandWorked(shard0Primary.adminCommand({setFeatureCompatibilityVersion: lastLTSFCV})); + + shard0Rst.nodes.forEach(node => { + // We expect every document except for the parent session documents to be deleted. + const collectionDocCount = node.getCollection(kConfigTxnNs).count(); + assert.eq(collectionDocCount, 2); + + const upsertedDoc = node.getCollection(kConfigTxnNs).findOne({"_id.id": sessionUUID}); + assert.eq(upsertedDoc.txnNum, NumberLong(8)); + assert.eq(upsertedDoc.lastWriteOpTime.ts, Timestamp(1, 0)); + assert.eq(upsertedDoc.lastWriteOpTime.t, NumberLong(1)); + + const unrelatedParentDocAfterDowngrade = + node.getCollection(kConfigTxnNs).findOne({"_id.id": unrelatedParentLsid.id}); + assert.eq(unrelatedParentDocAfterDowngrade, unrelatedParentDocBeforeDowngrade); + + // For newly upserted session documents, we use the current wall clock time as the + // lastWriteDate, thus we expect it to be farther ahead than the unchanged session document. + assert.gte(upsertedDoc.lastWriteDate, unrelatedParentDocAfterDowngrade.lastWriteDate); + }); + + st.stop(); +})(); + +(() => { + jsTest.log( + "Test downgrade does not modify a parent session document if it has a higher txnNumber " + + "than its children."); + + const st = new ShardingTest({shards: {rs0: {nodes: 2}}}); + const shard0Rst = st.rs0; + const shard0Primary = shard0Rst.getPrimary(); + + const kDbName = "testDb"; + const kCollName = "testColl"; + const testDB = shard0Primary.getDB(kDbName); + + const sessionUUID = UUID(); + const parentLsid = {id: sessionUUID}; + const childLsid0 = {id: sessionUUID, txnNumber: NumberLong(11), txnUUID: UUID()}; + const childLsid1 = {id: sessionUUID, txnNumber: NumberLong(12), txnUUID: UUID()}; + + // Start a parent session without related children. + assert.commandWorked(testDB.runCommand( + {insert: kCollName, documents: [{x: 0}], lsid: parentLsid, txnNumber: NumberLong(13)})); + + assert.commandWorked(testDB.runCommand({ + insert: kCollName, + documents: [{x: 1}], + lsid: childLsid0, + txnNumber: NumberLong(0), + startTransaction: true, + autocommit: false + })); + + assert.commandWorked(testDB.adminCommand( + {commitTransaction: 1, lsid: childLsid0, txnNumber: NumberLong(0), autocommit: false})); + + assert.commandWorked(testDB.runCommand({ + insert: kCollName, + documents: [{x: 2}], + lsid: childLsid1, + txnNumber: NumberLong(0), + startTransaction: true, + autocommit: false + })); + + const parentDocBeforeDowngrade = + shard0Primary.getCollection(kConfigTxnNs).findOne({"_id.id": sessionUUID}); + + assert.commandWorked(testDB.adminCommand( + {commitTransaction: 1, lsid: childLsid1, txnNumber: NumberLong(0), autocommit: false})); + + assert.commandWorked(shard0Primary.adminCommand({setFeatureCompatibilityVersion: lastLTSFCV})); + + shard0Rst.nodes.forEach(node => { + // We expect every document except for the parent session documents to be deleted. + const collectionDocCount = node.getCollection(kConfigTxnNs).count(); + assert.eq(collectionDocCount, 1); + + const parentDocAfterDowngrade = + shard0Primary.getCollection(kConfigTxnNs).findOne({"_id.id": sessionUUID}); + assert.eq(parentDocAfterDowngrade, parentDocBeforeDowngrade); + }); + + st.stop(); +})(); + +(() => { + jsTest.log("Test downgrade modifies a parent session document if it has a txnNumber equal to " + + " the highest txnNumber of its children."); + + const st = new ShardingTest({shards: {rs0: {nodes: 2}}}); + const shard0Rst = st.rs0; + const shard0Primary = shard0Rst.getPrimary(); + + const kDbName = "testDb"; + const kCollName = "testColl"; + const testDB = shard0Primary.getDB(kDbName); + + const sessionUUID = UUID(); + const parentLsid = {id: sessionUUID}; + const childLsid0 = {id: sessionUUID, txnNumber: NumberLong(13), txnUUID: UUID()}; + const childLsid1 = {id: sessionUUID, txnNumber: NumberLong(14), txnUUID: UUID()}; + + // Start a parent session without related children. + assert.commandWorked(testDB.runCommand( + {insert: kCollName, documents: [{x: 0}], lsid: parentLsid, txnNumber: NumberLong(14)})); + + assert.commandWorked(testDB.runCommand({ + insert: kCollName, + documents: [{x: 1}], + lsid: childLsid0, + txnNumber: NumberLong(0), + startTransaction: true, + autocommit: false + })); + + assert.commandWorked(testDB.adminCommand( + {commitTransaction: 1, lsid: childLsid0, txnNumber: NumberLong(0), autocommit: false})); + + assert.commandWorked(testDB.runCommand({ + insert: kCollName, + documents: [{x: 2}], + lsid: childLsid1, + txnNumber: NumberLong(0), + startTransaction: true, + autocommit: false + })); + + assert.commandWorked(testDB.adminCommand( + {commitTransaction: 1, lsid: childLsid1, txnNumber: NumberLong(0), autocommit: false})); + + const parentDocBeforeDowngrade = + shard0Primary.getCollection(kConfigTxnNs).findOne({"_id.id": sessionUUID}); + + assert.commandWorked(shard0Primary.adminCommand({setFeatureCompatibilityVersion: lastLTSFCV})); + + shard0Rst.nodes.forEach(node => { + // We expect every document except for the parent session documents to be deleted. + const collectionDocCount = node.getCollection(kConfigTxnNs).count(); + assert.eq(collectionDocCount, 1); + + const doc = node.getCollection(kConfigTxnNs).findOne({"_id.id": sessionUUID}); + assert.eq(doc.txnNum, NumberLong(14)); + assert.eq(doc.lastWriteOpTime.ts, Timestamp(1, 0)); + assert.eq(doc.lastWriteOpTime.t, NumberLong(1)); + assert.gte(doc.lastWriteDate, parentDocBeforeDowngrade.lastWriteDate); + }); + + st.stop(); +})(); +})(); |