0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-11-30 17:10:48 +01:00
mongodb/jstests/multiVersion/genericChangeStreams/change_streams_multi_version_transaction.js
Moustafa Maher 2fd5f78d5a SERVER-95421 make initiateWithHighElectionTimeout the default in ReplSetTest (#28174)
GitOrigin-RevId: df168ee363c3f0e86526270437d3688ac4bb326d
2024-10-22 02:53:25 +00:00

128 lines
5.6 KiB
JavaScript

import "jstests/multiVersion/libs/multi_rs.js";
import {ReplSetTest} from "jstests/libs/replsettest.js";
const dbName = jsTestName();
const watchedCollName = "change_stream_watched";
const unwatchedCollName = "change_stream_unwatched";
// Calls next() on a change stream cursor 'n' times and returns an array with the results.
function getChangeStreamResults(cursor, n) {
let results = [];
for (let i = 0; i < n; ++i) {
assert.soon(() => cursor.hasNext(), "Timed out waiting for change stream result " + i);
results.push(cursor.next());
}
assert(!cursor.hasNext()); // The change stream should always have exactly 'n' results.
return results;
}
// Compare expected changes with output from a change stream, failing an assertion if they do
// not match.
function compareChanges(expectedChanges, observedChanges) {
assert.eq(expectedChanges.length, observedChanges.length);
for (let i = 0; i < expectedChanges.length; ++i) {
assert.eq(expectedChanges[i].operationType, observedChanges[i].operationType);
if (expectedChanges[i].hasOwnProperty("fullDocument")) {
assert.eq(expectedChanges[i].fullDocument, observedChanges[i].fullDocument);
}
if (expectedChanges[i].hasOwnProperty("updateDescription")) {
assert.eq(expectedChanges[i].updateDescription, observedChanges[i].updateDescription);
}
if (expectedChanges[i].hasOwnProperty("documentKey")) {
assert.eq(expectedChanges[i].documentKey, observedChanges[i].documentKey);
}
}
}
// Opens a $changeStream and then performs inserts, deletes, updates both within a transaction
// and outside the transaction. Leaves all collections empty when done.
function performDBOps(mongod) {
const session = mongod.startSession();
session.startTransaction();
const watchedColl = session.getDatabase(dbName)[watchedCollName];
assert.commandWorked(watchedColl.insert({_id: 1}));
assert.commandWorked(watchedColl.updateOne({_id: 1}, {$set: {a: 1}}));
assert.commandWorked(watchedColl.remove({_id: 1}));
const unwatchedColl = session.getDatabase(dbName)[unwatchedCollName];
assert.commandWorked(unwatchedColl.insert({_id: 1}));
assert.commandWorked(unwatchedColl.remove({_id: 1}));
const watchedCollNoTxn = mongod.getDB(dbName)[watchedCollName];
assert.commandWorked(watchedCollNoTxn.insert({_id: 2}));
assert.commandWorked(watchedCollNoTxn.remove({_id: 2}));
session.commitTransaction();
}
// Resume a change stream from each of the resume tokens in the 'changeStreamDocs' array and
// verify that we always see the same set of changes.
function resumeChangeStreamFromEachToken(mongod, changeStreamDocs, expectedChanges) {
changeStreamDocs.forEach(function(changeDoc, i) {
const testDB = mongod.getDB(dbName);
const resumedCursor = testDB[watchedCollName].watch([], {resumeAfter: changeDoc._id});
// Resuming from document 'i' should return all the documents from 'i' + 1 to the end of
// the array.
const expectedChangesAfterResumeToken = expectedChanges.slice(i + 1);
compareChanges(
expectedChangesAfterResumeToken,
getChangeStreamResults(resumedCursor, expectedChangesAfterResumeToken.length));
});
}
function runTest(downgradeVersion) {
const rst = new ReplSetTest({
nodes: 2,
nodeOptions: {binVersion: downgradeVersion},
});
jsTestLog("Running test with 'downgradeVersion': " + downgradeVersion);
rst.startSet();
rst.initiate(null, null, {initiateWithDefaultElectionTimeout: true});
rst.getPrimary().getDB(dbName).createCollection(watchedCollName);
rst.getPrimary().getDB(dbName).createCollection(unwatchedCollName);
// Create the original change stream, verify it gives us the changes we expect, and verify that
// we can correctly resume from any resume token.
const changeStreamCursor = rst.getPrimary().getDB(dbName)[watchedCollName].watch();
performDBOps(rst.getPrimary());
// Starting with MongoDB 4.8 we expect update descriptions to include truncatedArrays.
const updateDescription = {updatedFields: {a: 1}, removedFields: []};
if (MongoRunner.compareBinVersions(downgradeVersion, "4.8") >= 0) {
updateDescription.truncatedArrays = [];
}
const expectedChanges = [
{operationType: "insert", fullDocument: {_id: 2}},
{operationType: "delete", documentKey: {_id: 2}},
{operationType: "insert", fullDocument: {_id: 1}},
{operationType: "update", updateDescription: updateDescription},
{operationType: "delete", documentKey: {_id: 1}},
];
const changeStreamDocs = getChangeStreamResults(changeStreamCursor, expectedChanges.length);
compareChanges(expectedChanges, changeStreamDocs);
resumeChangeStreamFromEachToken(rst.getPrimary(), changeStreamDocs, expectedChanges);
// Upgrade the replica set (while leaving featureCompatibilityVersion as it is) and verify that
// we can correctly resume from any resume token.
rst.upgradeSet({binVersion: "latest"});
resumeChangeStreamFromEachToken(rst.getPrimary(), changeStreamDocs, expectedChanges);
// Upgrade the featureCompatibilityVersion and verify that we can correctly resume from any
// resume token.
assert.commandWorked(
rst.getPrimary().adminCommand({setFeatureCompatibilityVersion: latestFCV, confirm: true}));
checkFCV(rst.getPrimary().getDB("admin"), latestFCV);
resumeChangeStreamFromEachToken(rst.getPrimary(), changeStreamDocs, expectedChanges);
rst.stopSet();
}
runTest("last-continuous");
runTest("last-lts");