diff options
author | Denis Grebennicov <denis.grebennicov@mongodb.com> | 2021-12-06 15:42:23 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-12-06 16:13:58 +0000 |
commit | 909c30b0ff8488299215ccd5ea96d6e3b625433d (patch) | |
tree | 85a01648589e132accb1e6f2bf3610046cfa9cf7 | |
parent | 23fe0c825d6be1a0f4127c1cf19c997590fc0c42 (diff) | |
download | mongo-909c30b0ff8488299215ccd5ea96d6e3b625433d.tar.gz |
SERVER-60237 Implement FCV upgrade/downgrade for change stream pre-/post-images
22 files changed, 334 insertions, 172 deletions
diff --git a/jstests/change_streams/change_stream_pre_image_lookup_whole_db_whole_cluster.js b/jstests/change_streams/change_stream_pre_image_lookup_whole_db_whole_cluster.js index fc6fb28680d..7017fc707c2 100644 --- a/jstests/change_streams/change_stream_pre_image_lookup_whole_db_whole_cluster.js +++ b/jstests/change_streams/change_stream_pre_image_lookup_whole_db_whole_cluster.js @@ -13,14 +13,14 @@ (function() { "use strict"; -load("jstests/libs/change_stream_util.js"); // For canRecordPreImagesInConfigDatabase. +load("jstests/libs/change_stream_util.js"); // For isChangeStreamPreAndPostImagesEnabled. load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers. const testDB = db.getSiblingDB(jsTestName()); const adminDB = db.getSiblingDB("admin"); const collWithPreImageName = "coll_with_pre_images"; const collWithNoPreImageName = "coll_with_no_pre_images"; -const canRecordPreImagesInConfigDb = canRecordPreImagesInConfigDatabase(testDB); +const canRecordPreImagesInConfigDb = isChangeStreamPreAndPostImagesEnabled(testDB); if (!canRecordPreImagesInConfigDb && FixtureHelpers.isMongos(db)) { jsTestLog("Skipping test as pre image lookup is not supported in sharded cluster with feature" + diff --git a/jstests/change_streams/change_streams_lookup_preimage_with_chunk_migration.js b/jstests/change_streams/change_streams_lookup_preimage_with_chunk_migration.js index d7555601788..2d3e91e1218 100644 --- a/jstests/change_streams/change_streams_lookup_preimage_with_chunk_migration.js +++ b/jstests/change_streams/change_streams_lookup_preimage_with_chunk_migration.js @@ -4,7 +4,6 @@ * * @tags: [ * featureFlagChangeStreamPreAndPostImages, - * featureFlagClusteredIndexes, * multiversion_incompatible, * requires_sharding, * uses_change_streams, diff --git a/jstests/change_streams/lookup_pit_pre_and_post_image.js b/jstests/change_streams/lookup_pit_pre_and_post_image.js index 411625ca9b2..2ff1b2cccd4 100644 --- a/jstests/change_streams/lookup_pit_pre_and_post_image.js +++ b/jstests/change_streams/lookup_pit_pre_and_post_image.js @@ -9,12 +9,12 @@ "use strict"; load("jstests/libs/collection_drop_recreate.js"); // For assertDropAndRecreateCollection. -load("jstests/libs/change_stream_util.js"); // For canRecordPreImagesInConfigDatabase. +load("jstests/libs/change_stream_util.js"); // For isChangeStreamPreAndPostImagesEnabled. const testDB = db.getSiblingDB(jsTestName()); const collName = "test"; -if (!canRecordPreImagesInConfigDatabase(testDB)) { +if (!isChangeStreamPreAndPostImagesEnabled(testDB)) { const coll = assertDropAndRecreateCollection(testDB, collName); // If feature flag is off, creating changeStream with new fullDocument arguments should throw. diff --git a/jstests/change_streams/lookup_pre_image.js b/jstests/change_streams/lookup_pre_image.js index a9ebbb73611..d7662939d04 100644 --- a/jstests/change_streams/lookup_pre_image.js +++ b/jstests/change_streams/lookup_pre_image.js @@ -14,11 +14,11 @@ "use strict"; load("jstests/libs/change_stream_util.js"); // For ChangeStreamTest and - // canRecordPreImagesInConfigDatabase. + // isChangeStreamPreAndPostImagesEnabled. load("jstests/libs/collection_drop_recreate.js"); // For assert[Drop|Create]Collection. load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers. -const canRecordPreImagesInConfigDb = canRecordPreImagesInConfigDatabase(db); +const canRecordPreImagesInConfigDb = isChangeStreamPreAndPostImagesEnabled(db); if (!canRecordPreImagesInConfigDb && FixtureHelpers.isMongos(db)) { jsTestLog("Skipping test as pre image lookup is not supported in sharded cluster with feature" + diff --git a/jstests/change_streams/write_pit_preimage.js b/jstests/change_streams/write_pit_preimage.js index 654edc958af..0fd797209c4 100644 --- a/jstests/change_streams/write_pit_preimage.js +++ b/jstests/change_streams/write_pit_preimage.js @@ -1,13 +1,10 @@ // Tests that pre-images are stored in the pre-images collection on updates and deletes in // collections with 'changeStreamPreAndPostImages' being enabled. // @tags: [ -// requires_fcv_51, +// requires_fcv_52, // featureFlagChangeStreamPreAndPostImages, -// # Clustered index support is required for change stream pre-images collection. -// featureFlagClusteredIndexes, // assumes_against_mongod_not_mongos, // change_stream_does_not_expect_txns, -// multiversion_incompatible, // ] (function() { "use strict"; @@ -51,8 +48,10 @@ function assertValidChangeStreamPreImageDocument(preImage) { function testFunc(collOptions = {}) { let coll = assertDropAndRecreateCollection(testDB, collName, collOptions); - assertDropCollection(configDB, preImagesCollName); + + // Ensure we test the behavior with a clean state. const preImagesColl = configDB.getCollection(preImagesCollName); + assert.commandWorked(preImagesColl.deleteMany({})); // Perform an insert and an update modification. assert.commandWorked(coll.insert(originalDoc)); diff --git a/jstests/core/timeseries/timeseries_collmod.js b/jstests/core/timeseries/timeseries_collmod.js index b1cc445e9b1..86374d30dbc 100644 --- a/jstests/core/timeseries/timeseries_collmod.js +++ b/jstests/core/timeseries/timeseries_collmod.js @@ -55,7 +55,7 @@ assert.commandFailedWithCode(db.runCommand({"collMod": collName, "recordPreImage assert.commandFailedWithCode( db.runCommand({"collMod": collName, "changeStreamPreAndPostImages": {enabled: true}}), [ ErrorCodes.InvalidOptions, - // TODO SERVER-52282: remove the error code. + // TODO SERVER-58584: remove the error code. 5846901 ]); diff --git a/jstests/core/timeseries/timeseries_create_collection.js b/jstests/core/timeseries/timeseries_create_collection.js index feb2f443d26..20de3e8974f 100644 --- a/jstests/core/timeseries/timeseries_create_collection.js +++ b/jstests/core/timeseries/timeseries_create_collection.js @@ -68,4 +68,18 @@ collections = assert.commandWorked(testDB.runCommand({listCollections: 1})).curs jsTestLog('Checking listCollections result: ' + tojson(collections)); assert.isnull(collections.find(entry => entry.name === 'system.buckets.' + coll.getName())); assert(collections.find(entry => entry.name === coll.getName())); + +// Should fail to create a timeseries collection with enabled 'changeStreamPreAndPostImages' +// option. +coll.drop(); +assert.commandFailedWithCode(testDB.runCommand({ + create: coll.getName(), + timeseries: {timeField: timeFieldName}, + changeStreamPreAndPostImages: {enabled: true} +}), + [ + ErrorCodes.InvalidOptions, + // TODO SERVER-58584: remove the error code. + 5846901 + ]); })(); diff --git a/jstests/libs/change_stream_util.js b/jstests/libs/change_stream_util.js index c79accceac2..e0fd8a0a93e 100644 --- a/jstests/libs/change_stream_util.js +++ b/jstests/libs/change_stream_util.js @@ -45,18 +45,6 @@ function isChangeStreamsRewriteEnabled(db) { } /** - * Returns true if pre-images can be recorded in 'system.preimages' collection, false otherwise. - */ -function canRecordPreImagesInConfigDatabase(db) { - // Clustered index feature must be enabled to record pre-images in 'system.preimages' - // collection. - const getParam = db.adminCommand({getParameter: 1, featureFlagClusteredIndexes: 1}); - return isChangeStreamPreAndPostImagesEnabled(db) && - getParam.hasOwnProperty("featureFlagClusteredIndexes") && - getParam.featureFlagClusteredIndexes.value; -} - -/** * Helper function used internally by ChangeStreamTest. If no passthrough is active, it is exactly * the same as calling db.runCommand. If a passthrough is active and has defined a function * 'changeStreamPassthroughAwareRunCommand', then this method will be overridden to allow individual @@ -578,3 +566,34 @@ function assertChangeStreamPreAndPostImagesCollectionOptionIsAbsent(db, collName const collectionInfos = db.getCollectionInfos({name: collName}); assert(!collectionInfos[0].options.hasOwnProperty("changeStreamPreAndPostImages")); } + +function findPreImagesCollectionDescriptions(db) { + return db.getSiblingDB("config").runCommand("listCollections", + {filter: {name: "system.preimages"}}); +} + +/** + * Asserts that pre-images collection is absent in configDB. + */ +function assertPreImagesCollectionIsAbsent(db) { + const result = findPreImagesCollectionDescriptions(db); + assert.eq(result.cursor.firstBatch.length, 0); +} + +/** + * Asserts that pre-images collection is created in the configDB and has clustered index on _id. + */ +function assertPreImagesCollectionExists(db) { + const collectionInfos = findPreImagesCollectionDescriptions(db); + assert.eq(collectionInfos.cursor.firstBatch.length, 1, collectionInfos); + const preImagesCollectionDescription = collectionInfos.cursor.firstBatch[0]; + assert.eq(preImagesCollectionDescription.name, "system.preimages"); + + // Verifies that the pre-images collection is clustered by _id. + assert(preImagesCollectionDescription.hasOwnProperty("options"), + preImagesCollectionDescription); + assert(preImagesCollectionDescription.options.hasOwnProperty("clusteredIndex"), + preImagesCollectionDescription); + const clusteredIndexDescription = preImagesCollectionDescription.options.clusteredIndex; + assert(clusteredIndexDescription, preImagesCollectionDescription); +} diff --git a/jstests/libs/check_unique_indexes.js b/jstests/libs/check_unique_indexes.js index 9decdbded64..b64532c98f8 100644 --- a/jstests/libs/check_unique_indexes.js +++ b/jstests/libs/check_unique_indexes.js @@ -29,7 +29,7 @@ function checkUniqueIndexFormatVersion(adminDB) { let currentCollection = currentDatabase.getCollection(c.name); currentCollection.getIndexes().forEach(function(index) { - if (index.unique) { + if (index.unique && !index.clustered) { let ifv = currentCollection.aggregate({$collStats: {storageStats: {}}}) .next() .storageStats.indexDetails[index.name] diff --git a/jstests/multiVersion/change_streams_pre_and_post_images_upgrade_downgrade.js b/jstests/multiVersion/change_streams_pre_and_post_images_upgrade_downgrade.js index 60aaf51f050..da6d6e1f957 100644 --- a/jstests/multiVersion/change_streams_pre_and_post_images_upgrade_downgrade.js +++ b/jstests/multiVersion/change_streams_pre_and_post_images_upgrade_downgrade.js @@ -1,10 +1,9 @@ /** * Verifies that it is possible to upgrade a replica set with collections with 'recordPreImages' * option to use 'changeStreamPreAndPostImages' option, and to do a corresponding downgrade. - * @tags: [requires_fcv_51, + * @tags: [ + * requires_fcv_52, * featureFlagChangeStreamPreAndPostImages, - * # Clustered index support is required for change stream pre-images collection. - * featureFlagClusteredIndexes, * ] */ (function() { @@ -18,15 +17,27 @@ load( "jstests/libs/change_stream_util.js"); // For // assertChangeStreamPreAndPostImagesCollectionOptionIsEnabled, // assertChangeStreamPreAndPostImagesCollectionOptionIsAbsent. + // assertPreImagesCollectionIsAbsent, + // assertPreImagesCollectionExists. +load("jstests/libs/fail_point_util.js"); // For configureFailPoint. const collName = "test"; +const latestBinVersion = "latest"; -function runTest(downgradeVersion) { - const downgradeFCV = binVersionToFCV(downgradeVersion); +// Checks that the pre-image of the next change event in the change stream equals to the +// 'expectedPreImage'. +function assertNextPreImage(changeStream, expectedPreImage) { + assert.soon(() => changeStream.hasNext()); + assert.eq(changeStream.next().fullDocumentBeforeChange, expectedPreImage); +} +// Tests "changeStreamPreAndPostImages" option for the "create" and "collMod" commands in downgraded +// and upgraded FCV states. Tests an FCV downgrade succeeds when no collection with +// changeStreamPreImages: {enabled: true} exists. +function testCreateAndCollModCommandsInUpgradedDowngradedFCVStates(downgradeFCV) { const rst = new ReplSetTest({ nodes: 2, - nodeOptions: {binVersion: downgradeVersion}, + nodeOptions: {binVersion: downgradeFCV}, }); rst.startSet(); rst.initiate(); @@ -36,7 +47,7 @@ function runTest(downgradeVersion) { assertCreateCollection(testDB, collName, {"recordPreImages": true}); // Upgrade the replica set. - rst.upgradeSet({binVersion: "latest"}); + rst.upgradeSet({binVersion: latestBinVersion}); testDB = rst.getPrimary().getDB(jsTestName()); // Verify that an attempt to set 'changeStreamPreAndPostImages' option fails for the downgraded @@ -50,7 +61,7 @@ function runTest(downgradeVersion) { 5846901); // Set the FCV to the latest. - testDB.adminCommand({setFeatureCompatibilityVersion: latestFCV}); + assert.commandWorked(testDB.adminCommand({setFeatureCompatibilityVersion: latestFCV})); // 'changeStreamPreAndPostImages' option must be absent and 'recordPreImages' option must be set // to true. @@ -74,19 +85,182 @@ function runTest(downgradeVersion) { assertChangeStreamPreAndPostImagesCollectionOptionIsAbsent(testDB, collName); // Downgrade the FCV. - testDB.adminCommand({setFeatureCompatibilityVersion: downgradeFCV}); + assert.commandWorked(testDB.adminCommand({setFeatureCompatibilityVersion: downgradeFCV})); + + // Downgrade the replica set. + rst.upgradeSet({binVersion: downgradeFCV}); + rst.stopSet(); +} + +// Tests that when change stream pre-images are recorded on a collection using option +// recordPreImages: true and, after FCV upgrade, recordPreImages: true option is replaced with +// changeStreamPreAndPostImages: {enabled: true} , then pre-images are available for all change +// events in a change stream without interruption. Subsequently, tests a FCV downgrade and switching +// back from option changeStreamPreAndPostImages: {enabled: true} to recordPreImages: true. +function testUpgradeDowngradeFromRecordPreImageOptionToChangeStreamPreAndPostImages(downgradeFCV) { + // Upgrade scenario. + // Upgrade server binary. + const rst = new ReplSetTest({ + nodes: 2, + nodeOptions: {binVersion: downgradeFCV}, + }); + rst.startSet(); + rst.initiate(); + rst.upgradeSet({binVersion: latestBinVersion}); + + // Create the collection with recorded pre-images enabled and insert one document. + const testDB = rst.getPrimary().getDB(jsTestName()); + const coll = assertCreateCollection(testDB, collName, {recordPreImages: true}); + assert.commandWorked(coll.insert({_id: 1, eventId: 1})); + + // Open a change stream with fullDocumentBeforeChange: "required". + const changeStream = coll.watch([], {fullDocumentBeforeChange: "required"}); + + // Perform an "update" command. Pre-image will be recorded in the oplog. + assert.commandWorked(coll.update({_id: 1}, {$inc: {eventId: 1}})); + + // Upgrade to the latest FCV. + assert.commandWorked(testDB.adminCommand({setFeatureCompatibilityVersion: latestFCV})); + + // Verify that the pre-images collection is created. + assertPreImagesCollectionExists(testDB); + + // Enable change stream pre-images recording for the collection. + assert.commandWorked( + testDB.runCommand({collMod: collName, changeStreamPreAndPostImages: {enabled: true}})); + + // Issue an "update" command for which the pre-image won't be available after the FCV downgrade. + assert.commandWorked(coll.update({_id: 1}, {$inc: {eventId: 1}})); + + // Verify that change stream receives change event with pre-image being set. + assertNextPreImage(changeStream, {_id: 1, eventId: 1}); + + // Issue an "update" command for which the pre-image won't be available after the FCV downgrade. + assert.commandWorked(coll.update({_id: 1}, {$inc: {eventId: 1}})); + + // Downgrade scenario. + // Revert to the previous pre-image recording capability available in 5.0. + assert.commandWorked(testDB.runCommand({collMod: collName, recordPreImages: true})); + + // Verify that the change stream returns a change event with a pre-image set. + assertNextPreImage(changeStream, {_id: 1, eventId: 2}); + + // Downgrade the FCV version. Pre-images collection is dropped during the downgrade. + assert.commandWorked(testDB.adminCommand({setFeatureCompatibilityVersion: downgradeFCV})); + + // Verify that pre-images collection is dropped. + assertPreImagesCollectionIsAbsent(testDB); + + // Verify that reading the next change event fails for change stream with + // fullDocumentBeforeChange: "required", as pre-image for this event was recorded in the + // pre-images collection. + assert.throwsWithCode(() => changeStream.hasNext(), ErrorCodes.NoMatchingDocument); + + rst.stopSet(); +} + +// Tests that when change stream pre-images are recorded on a collection using option +// changeStreamPreAndPostImages: {enabled: true} and, after changeStreamPreAndPostImages option is +// disabled, then pre-images are unavailable to change stream change events after FCV downgrade. +function testDowngrade(downgradeFCV) { + const rst = new ReplSetTest({ + nodes: 2, + nodeOptions: {binVersion: latestBinVersion}, + }); + rst.startSet(); + rst.initiate(); + + // Create the collection with changeStreamPreAndPostImages: {enabled: true} and perform insert + // and update operations. + const testDB = rst.getPrimary().getDB(jsTestName()); + const coll = + assertCreateCollection(testDB, collName, {changeStreamPreAndPostImages: {enabled: true}}); + const changeStream = coll.watch([], {fullDocumentBeforeChange: "required"}); + assert.commandWorked(coll.insert({_id: 1, eventId: 1})); + assert.commandWorked(coll.update({_id: 1}, {$inc: {eventId: 1}})); + + // Downgrade scenario. + // Issue "collMod" command in order to disable changeStreamPreAndPostImages option. + assert.commandWorked( + testDB.runCommand({"collMod": collName, changeStreamPreAndPostImages: {enabled: false}})); + + // Downgrade the FCV version. + assert.commandWorked(testDB.adminCommand({setFeatureCompatibilityVersion: downgradeFCV})); + + // Verify that the pre-images collection is dropped. + assertPreImagesCollectionIsAbsent(testDB); + + // Verify that reading the next change event fails for change stream with + // fullDocumentBeforeChange: "required", as pre-image for this event was recorded in the + // pre-images collection that no longer exists. + assert.throwsWithCode(() => changeStream.hasNext(), ErrorCodes.NoMatchingDocument); + + rst.stopSet(); +} + +// Tests that downgrading of the FCV fails if there exists a collection with +// changeStreamPreAndPostImages: {enabled: true}. +function testFCVDowngradeFailureWhenChangeStreamPreAndPostImagesEnabledForCollection(downgradeFCV) { + const rst = new ReplSetTest({ + nodes: 2, + nodeOptions: {binVersion: latestBinVersion}, + }); + rst.startSet(); + rst.initiate(); + const testDB = rst.getPrimary().getDB(jsTestName()); + + // Pre-images collection must exist upon start-up with the latest FCV. + assertPreImagesCollectionExists(testDB); + assert.commandWorked( + testDB.createCollection("testCollection", {changeStreamPreAndPostImages: {enabled: true}})); + + // Verify that a downgrade of the FCV fails when there is at least one collection with + // {changeStreamPreAndPostImages: {enabled: true}} option set. + assert.commandFailedWithCode( + testDB.adminCommand({setFeatureCompatibilityVersion: downgradeFCV}), + ErrorCodes.CannotDowngrade); + + // Verify that the pre-images collection is not dropped in case of a failed FCV downgrade. + assertPreImagesCollectionExists(testDB); + + rst.stopSet(); +} + +// Tests that FCV upgrade fails if there is an error creating pre-images collection. +function testFCVUpgradeFailureWhenCreationOfPreImagesCollectionFails(downgradeFCV) { + const rst = new ReplSetTest({ + nodes: 2, + nodeOptions: {binVersion: binVersionFromFCV(downgradeFCV)}, + }); + rst.startSet(); + rst.initiate(); + rst.upgradeSet({binVersion: latestBinVersion}); + const testDB = rst.getPrimary().getDB(jsTestName()); + configureFailPoint(rst.getPrimary(), "failPreimagesCollectionCreation", {}, {times: 1}); + + // Verify that FCV upgrade fails when creation of the pre-images collection fails. + assert.commandFailedWithCode(testDB.adminCommand({setFeatureCompatibilityVersion: latestFCV}), + 5868501); + + // Verfiy that FCV version remains unchanged. + const fcvDoc = testDB.adminCommand({getParameter: 1, featureCompatibilityVersion: 1}); + assert.eq(fcvDoc.featureCompatibilityVersion.version, downgradeFCV, fcvDoc); - // Downgrading the cluster should fail, since the pre-images collection is clustered which is - // not supported by the downgraded binary. - try { - rst.upgradeSet({binVersion: downgradeVersion}); - assert(false); - } catch (exception) { - assert.eq(exception.returnCode, MongoRunner.EXIT_UNCAUGHT); - } + // Verify that the pre-images collection is not created. + assertPreImagesCollectionIsAbsent(testDB); rst.stopSet(); } -runFeatureFlagMultiversionTest('featureFlagChangeStreamPreAndPostImages', runTest); +runFeatureFlagMultiversionTest('featureFlagChangeStreamPreAndPostImages', + testCreateAndCollModCommandsInUpgradedDowngradedFCVStates); +runFeatureFlagMultiversionTest( + 'featureFlagChangeStreamPreAndPostImages', + testUpgradeDowngradeFromRecordPreImageOptionToChangeStreamPreAndPostImages); +runFeatureFlagMultiversionTest('featureFlagChangeStreamPreAndPostImages', testDowngrade); +runFeatureFlagMultiversionTest( + 'featureFlagChangeStreamPreAndPostImages', + testFCVDowngradeFailureWhenChangeStreamPreAndPostImagesEnabledForCollection); +runFeatureFlagMultiversionTest('featureFlagChangeStreamPreAndPostImages', + testFCVUpgradeFailureWhenCreationOfPreImagesCollectionFails); })(); diff --git a/jstests/noPassthrough/change_stream_preimages_standalone_mode.js b/jstests/noPassthrough/change_stream_preimages_standalone_mode.js index fb9664f17dd..84c8a9a2f0a 100644 --- a/jstests/noPassthrough/change_stream_preimages_standalone_mode.js +++ b/jstests/noPassthrough/change_stream_preimages_standalone_mode.js @@ -10,8 +10,6 @@ * requires_replication, * requires_fcv_52, * featureFlagChangeStreamPreAndPostImages, - * # Clustered index support is required for change stream pre-images collection. - * featureFlagClusteredIndexes, * # TODO SERVER-58694: remove this tag. * change_stream_does_not_expect_txns, * ] diff --git a/jstests/noPassthrough/change_streams_pre_and_post_images_in_create_and_collmod.js b/jstests/noPassthrough/change_streams_pre_and_post_images_in_create_and_collmod.js index 2416d7a6a74..6b82ce1d7b7 100644 --- a/jstests/noPassthrough/change_streams_pre_and_post_images_in_create_and_collmod.js +++ b/jstests/noPassthrough/change_streams_pre_and_post_images_in_create_and_collmod.js @@ -1,22 +1,17 @@ /* * Tests that the 'changeStreamPreAndPostImages' option is settable via the collMod and create * commands. Also tests that this option cannot be set on collections in the 'local', 'admin', - * 'config' databases as well as timeseries and view collections. Verifies that the pre-images - * collection is clustered. + * 'config' databases as well as on view collections. * @tags: [ - * requires_fcv_51, + * requires_fcv_52, * featureFlagChangeStreamPreAndPostImages, - * # Clustered index support is required for change stream pre-images collection. - * featureFlagClusteredIndexes, * ] */ (function() { 'use strict'; -load("jstests/libs/collection_options.js"); // For assertCollectionOptionIsEnabled, - // assertCollectionOptionIsAbsent. -load("jstests/libs/collection_drop_recreate.js"); // For assertDropCollection. -load("jstests/libs/fail_point_util.js"); // For configureFailPoint, off. +load("jstests/libs/collection_options.js"); // For assertCollectionOptionIsEnabled, + // assertCollectionOptionIsAbsent. load( "jstests/libs/change_stream_util.js"); // For // assertChangeStreamPreAndPostImagesCollectionOptionIsEnabled, @@ -31,13 +26,7 @@ const collName = 'changeStreamPreAndPostImages'; const collName2 = 'changeStreamPreAndPostImages2'; const collName3 = 'changeStreamPreAndPostImages3'; const collName4 = 'changeStreamPreAndPostImages4'; -const collName5 = 'changeStreamPreAndPostImages5'; -const collName6 = 'changeStreamPreAndPostImages6'; const viewName = "view"; -const preImagesCollName = "system.preimages"; -const createTimeseriesOptions = { - timeField: "a" -}; const primary = rsTest.getPrimary(); const adminDB = primary.getDB("admin"); @@ -45,34 +34,6 @@ const localDB = primary.getDB("local"); const configDB = primary.getDB("config"); const testDB = primary.getDB(dbName); -function findPreImagesCollectionDescriptions() { - return configDB.runCommand("listCollections", {filter: {name: preImagesCollName}}); -} - -function assertPreImagesCollectionIsAbsent() { - const result = findPreImagesCollectionDescriptions(); - assert.eq(result.cursor.firstBatch.length, 0); -} - -function assertPreImagesCollectionExists() { - const result = findPreImagesCollectionDescriptions(); - assert.eq(result.cursor.firstBatch[0].name, preImagesCollName); -} - -// Verifies that the pre-images collection is clustered by _id. -function assertPreImagesCollectionIsClustered() { - const collectionInfos = findPreImagesCollectionDescriptions(); - assert.eq(collectionInfos.cursor.firstBatch.length, 1, collectionInfos); - const preImagesCollectionDescription = collectionInfos.cursor.firstBatch[0]; - assert(preImagesCollectionDescription.hasOwnProperty("options"), - preImagesCollectionDescription); - assert(preImagesCollectionDescription.options.hasOwnProperty("clusteredIndex"), - preImagesCollectionDescription); - const clusteredIndexDescription = preImagesCollectionDescription.options.clusteredIndex; - assert.eq(clusteredIndexDescription.unique, true, preImagesCollectionDescription); - assert.eq(clusteredIndexDescription.key, {_id: 1}, preImagesCollectionDescription); -} - // Check that we cannot set 'changeStreamPreAndPostImages' on the local, admin and config databases. for (const db of [localDB, adminDB, configDB]) { assert.commandFailedWithCode( @@ -85,42 +46,26 @@ for (const db of [localDB, adminDB, configDB]) { ErrorCodes.InvalidOptions); } -// Drop the pre-images collection. -assertDropCollection(configDB, preImagesCollName); -assertPreImagesCollectionIsAbsent(); - // Should be able to enable the 'changeStreamPreAndPostImages' via create or collMod. assert.commandWorked( testDB.runCommand({create: collName, changeStreamPreAndPostImages: {enabled: true}})); assertChangeStreamPreAndPostImagesCollectionOptionIsEnabled(testDB, collName); -assertPreImagesCollectionExists(); -assertPreImagesCollectionIsClustered(); - -// Drop the pre-images collection. -assertDropCollection(configDB, preImagesCollName); -assertPreImagesCollectionIsAbsent(); assert.commandWorked(testDB.runCommand({create: collName2})); assert.commandWorked( testDB.runCommand({collMod: collName2, changeStreamPreAndPostImages: {enabled: true}})); assertChangeStreamPreAndPostImagesCollectionOptionIsEnabled(testDB, collName2); -assertPreImagesCollectionExists(); // Verify that setting collection options with 'collMod' command does not affect // 'changeStreamPreAndPostImages' option. assert.commandWorked(testDB.runCommand({"collMod": collName2, validationLevel: "off"})); assertChangeStreamPreAndPostImagesCollectionOptionIsEnabled(testDB, collName2); -assertPreImagesCollectionExists(); // Should successfully disable 'changeStreamPreAndPostImages' using the 'collMod' command. assert.commandWorked( testDB.runCommand({collMod: collName2, changeStreamPreAndPostImages: {enabled: false}})); assertChangeStreamPreAndPostImagesCollectionOptionIsAbsent(testDB, collName2); -// Should not remove the pre-images collection on disabling 'changeStreamPreAndPostImages' -// option. -assertPreImagesCollectionExists(); - // Both 'recordPreImages' and 'changeStreamPreAndPostImages' may not be enabled at the same // time. assert.commandFailedWithCode( @@ -164,19 +109,11 @@ assert.commandWorked( assertCollectionOptionIsAbsent(testDB, collName3, "recordPreImages"); assertChangeStreamPreAndPostImagesCollectionOptionIsEnabled(testDB, collName3); -// Should fail to create a timeseries collection with enabled 'changeStreamPreAndPostImages' -// option. -assert.commandFailedWithCode(testDB.runCommand({ - create: collName4, - timeseries: createTimeseriesOptions, - changeStreamPreAndPostImages: {enabled: true} -}), - ErrorCodes.InvalidOptions); - -assert.commandWorked(testDB.runCommand({create: collName4, timeseries: createTimeseriesOptions})); -assert.commandFailedWithCode( - testDB.runCommand({collMod: collName4, changeStreamPreAndPostImages: {enabled: true}}), - ErrorCodes.InvalidOptions); +// Should set 'recordPreImages' to true and disable 'changeStreamPreAndPostImages' option. +assert.commandWorked(testDB.runCommand( + {create: collName4, recordPreImages: true, changeStreamPreAndPostImages: {enabled: false}})); +assert.commandWorked(testDB.runCommand( + {collMod: collName4, recordPreImages: true, changeStreamPreAndPostImages: {enabled: false}})); assertChangeStreamPreAndPostImagesCollectionOptionIsAbsent(testDB, collName4); // Should fail to create a view with enabled 'changeStreamPreAndPostImages' option. @@ -189,22 +126,5 @@ assert.commandFailedWithCode( testDB.runCommand({collMod: viewName, changeStreamPreAndPostImages: {enabled: true}}), ErrorCodes.InvalidOptions); -// Should fail to run 'create' and 'collMod' commands if creating pre-images collection fails. -const failpoint = configureFailPoint(primary, "failPreimagesCollectionCreation"); -assert.commandFailedWithCode( - testDB.runCommand({create: collName5, changeStreamPreAndPostImages: {enabled: true}}), 5868501); -assert.commandWorked(testDB.runCommand({create: collName5})); -assert.commandFailedWithCode( - testDB.runCommand({collMod: collName5, changeStreamPreAndPostImages: {enabled: true}}), - 5868501); -failpoint.off(); - -// Should set 'recordPreImages' to true and disable 'changeStreamPreAndPostImages' option. -assert.commandWorked(testDB.runCommand( - {create: collName6, recordPreImages: true, changeStreamPreAndPostImages: {enabled: false}})); -assert.commandWorked(testDB.runCommand( - {collMod: collName6, recordPreImages: true, changeStreamPreAndPostImages: {enabled: false}})); -assertChangeStreamPreAndPostImagesCollectionOptionIsAbsent(testDB, collName6); - rsTest.stopSet(); }()); diff --git a/jstests/noPassthrough/change_streams_pre_image_removal_job.js b/jstests/noPassthrough/change_streams_pre_image_removal_job.js index a256f7bdc50..cb9ba2e8c17 100644 --- a/jstests/noPassthrough/change_streams_pre_image_removal_job.js +++ b/jstests/noPassthrough/change_streams_pre_image_removal_job.js @@ -2,13 +2,10 @@ // removed from the pre-images collection via the 'PeriodicChangeStreamExpiredPreImagesRemover' // periodic job. // @tags: [ -// requires_fcv_51, +// requires_fcv_52, // featureFlagChangeStreamPreAndPostImages, -// # Clustered index support is required for change stream pre-images collection. -// featureFlagClusteredIndexes, // assumes_against_mongod_not_mongos, // change_stream_does_not_expect_txns, -// multiversion_incompatible, // ] (function() { "use strict"; diff --git a/jstests/replsets/change_stream_pit_pre_images.js b/jstests/replsets/change_stream_pit_pre_images.js index 6ef8f6fedbe..726507d0fcf 100644 --- a/jstests/replsets/change_stream_pit_pre_images.js +++ b/jstests/replsets/change_stream_pit_pre_images.js @@ -9,8 +9,6 @@ * requires_wiredtiger, * requires_fcv_52, * featureFlagChangeStreamPreAndPostImages, - * # Clustered index support is required for change stream pre-images collection. - * featureFlagClusteredIndexes, * ] */ (function() { diff --git a/jstests/sharding/printShardingStatus.js b/jstests/sharding/printShardingStatus.js index da4081c2ca2..5bd305da1d2 100644 --- a/jstests/sharding/printShardingStatus.js +++ b/jstests/sharding/printShardingStatus.js @@ -99,7 +99,7 @@ var config = mongos.getDB("config"); var configCopy = standalone.getDB("configCopy"); config.getCollectionInfos().forEach(function(c) { // It's illegal to copy the system collections. - if (c.name == "system.indexBuilds") { + if (["system.indexBuilds", "system.preimages"].includes(c.name)) { return; } diff --git a/src/mongo/db/catalog/clustered_collection_util.cpp b/src/mongo/db/catalog/clustered_collection_util.cpp index 7a7fb99a2b0..382357ea7b0 100644 --- a/src/mongo/db/catalog/clustered_collection_util.cpp +++ b/src/mongo/db/catalog/clustered_collection_util.cpp @@ -87,7 +87,7 @@ boost::optional<ClusteredCollectionInfo> parseClusteredInfo(const BSONElement& e } bool requiresLegacyFormat(const NamespaceString& nss) { - return nss.isTimeseriesBucketsCollection(); + return nss.isTimeseriesBucketsCollection() || nss.isChangeStreamPreImagesCollection(); } BSONObj formatClusterKeyForListIndexes(const ClusteredCollectionInfo& collInfo) { diff --git a/src/mongo/db/catalog/coll_mod.cpp b/src/mongo/db/catalog/coll_mod.cpp index 95c811f2bf0..7205ec2b764 100644 --- a/src/mongo/db/catalog/coll_mod.cpp +++ b/src/mongo/db/catalog/coll_mod.cpp @@ -664,12 +664,6 @@ Status _collModInternal(OperationContext* opCtx, LOGV2(5324200, "CMD: collMod", "cmdObj"_attr = cmd.toBSON(BSONObj())); } - if (cmrNew.changeStreamPreAndPostImagesOptions.has_value() && - cmrNew.changeStreamPreAndPostImagesOptions->getEnabled()) { - // Create pre-images collection if it doesn't already exist. - createChangeStreamPreImagesCollection(opCtx); - } - // With exclusive access to the collection, we can take ownership of the modified docs observed // by the side write tracker if a unique index conversion is requested. // This step releases the resources associated with the token and therefore should not be diff --git a/src/mongo/db/catalog/create_collection.cpp b/src/mongo/db/catalog/create_collection.cpp index 050859699e8..a7bb318d94b 100644 --- a/src/mongo/db/catalog/create_collection.cpp +++ b/src/mongo/db/catalog/create_collection.cpp @@ -477,7 +477,7 @@ Status _createCollection(OperationContext* opCtx, << nss); } - if ((nss.isTimeseriesBucketsCollection()) != (clusteredIndex->getLegacyFormat())) { + if (clustered_util::requiresLegacyFormat(nss) != clusteredIndex->getLegacyFormat()) { return Status(ErrorCodes::Error(5979703), "The 'clusteredIndex' legacy format {clusteredIndex: <bool>} is only " "supported for specific internal collections and vice versa"); @@ -576,16 +576,6 @@ Status createCollection(OperationContext* opCtx, str::stream() << "Cannot create system collection " << ns << " within a transaction.", !opCtx->inMultiDocumentTransaction() || !ns.isSystem()); - if (options.changeStreamPreAndPostImagesOptions.getEnabled()) { - tassert(5868500, - "ChangeStreamPreAndPostImages feature flag must be enabled", - feature_flags::gFeatureFlagChangeStreamPreAndPostImages.isEnabled( - serverGlobalParams.featureCompatibility)); - - // Create preimages collection if it doesn't already exist. - createChangeStreamPreImagesCollection(opCtx); - } - return _createCollection(opCtx, ns, std::move(options), idIndex); } } @@ -659,16 +649,13 @@ void createChangeStreamPreImagesCollection(OperationContext* opCtx) { uassert(5868501, "Failpoint failPreimagesCollectionCreation enabled. Throwing exception", !MONGO_unlikely(failPreimagesCollectionCreation.shouldFail())); - tassert(5882500, - "Failed to create the pre-images collection: clustered indexes feature is not enabled", - feature_flags::gClusteredIndexes.isEnabled(serverGlobalParams.featureCompatibility)); const auto nss = NamespaceString::kChangeStreamPreImagesNamespace; CollectionOptions preImagesCollectionOptions; // Make the collection clustered by _id. preImagesCollectionOptions.clusteredIndex.emplace( - clustered_util::makeDefaultClusteredIdIndex()); + clustered_util::makeCanonicalClusteredInfoForLegacyFormat()); const auto status = _createCollection(opCtx, nss, std::move(preImagesCollectionOptions), BSONObj()); uassert(status.code(), diff --git a/src/mongo/db/commands/create_command.cpp b/src/mongo/db/commands/create_command.cpp index c51145ddf2a..b7751f0f270 100644 --- a/src/mongo/db/commands/create_command.cpp +++ b/src/mongo/db/commands/create_command.cpp @@ -285,12 +285,19 @@ public: cmd.setIdIndex(idIndexSpec); } + const auto isChangeStreamPreAndPostImagesEnabled = + (cmd.getChangeStreamPreAndPostImages() && + cmd.getChangeStreamPreAndPostImages()->getEnabled()); + + // Acquire shared lock on FCV if 'changeStreamPreAndPostImages' is enabled. + boost::optional<FixedFCVRegion> fcvRegion; + if (isChangeStreamPreAndPostImagesEnabled) { + fcvRegion.emplace(opCtx); + } + if (feature_flags::gFeatureFlagChangeStreamPreAndPostImages.isEnabled( serverGlobalParams.featureCompatibility)) { const auto isRecordPreImagesEnabled = cmd.getRecordPreImages().get_value_or(false); - const auto isChangeStreamPreAndPostImagesEnabled = - (cmd.getChangeStreamPreAndPostImages() && - cmd.getChangeStreamPreAndPostImages()->getEnabled()); uassert(ErrorCodes::InvalidOptions, "'recordPreImages' and 'changeStreamPreAndPostImages.enabled' can not be " "set to true simultaneously", diff --git a/src/mongo/db/commands/dbcommands.cpp b/src/mongo/db/commands/dbcommands.cpp index ec54c6c5ce0..78b5f25c93c 100644 --- a/src/mongo/db/commands/dbcommands.cpp +++ b/src/mongo/db/commands/dbcommands.cpp @@ -534,13 +534,19 @@ public: const RequestParser& requestParser, BSONObjBuilder& result) final { const auto* cmd = &requestParser.request(); + const auto isChangeStreamPreAndPostImagesEnabled = + (cmd->getChangeStreamPreAndPostImages() && + cmd->getChangeStreamPreAndPostImages()->getEnabled()); + + // Acquire shared lock on FCV if 'changeStreamPreAndPostImages' is enabled. + boost::optional<FixedFCVRegion> fcvRegion; + if (isChangeStreamPreAndPostImagesEnabled) { + fcvRegion.emplace(opCtx); + } if (feature_flags::gFeatureFlagChangeStreamPreAndPostImages.isEnabled( serverGlobalParams.featureCompatibility)) { const auto isRecordPreImagesEnabled = cmd->getRecordPreImages().get_value_or(false); - const auto isChangeStreamPreAndPostImagesEnabled = - (cmd->getChangeStreamPreAndPostImages() && - cmd->getChangeStreamPreAndPostImages()->getEnabled()); uassert(ErrorCodes::InvalidOptions, "'recordPreImages' and 'changeStreamPreAndPostImages.enabled' can not be set " "to true simultaneously", diff --git a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp index 3965c5eaa51..311036f4a32 100644 --- a/src/mongo/db/commands/set_feature_compatibility_version_command.cpp +++ b/src/mongo/db/commands/set_feature_compatibility_version_command.cpp @@ -36,6 +36,7 @@ #include "mongo/db/auth/authorization_session.h" #include "mongo/db/catalog/coll_mod.h" #include "mongo/db/catalog/collection_catalog_helper.h" +#include "mongo/db/catalog/create_collection.h" #include "mongo/db/catalog/database.h" #include "mongo/db/catalog/database_holder.h" #include "mongo/db/catalog/drop_collection.h" @@ -398,6 +399,7 @@ private: mongo::multiversion::FeatureCompatibilityVersion originalVersion, const SetFeatureCompatibilityVersion& request, boost::optional<Timestamp> changeTimestamp) { + const auto requestedVersion = request.getCommandParameter(); if (serverGlobalParams.clusterRole == ClusterRole::ConfigServer) { // Tell the shards to enter phase-1 of setFCV @@ -480,7 +482,6 @@ private: opCtx, CommandHelpers::appendMajorityWriteConcern(requestPhase2.toBSON({})))); // TODO: Remove once FCV 6.0 becomes last-lts - const auto requestedVersion = request.getCommandParameter(); if (!feature_flags::gFeatureFlagLongCollectionNames.isEnabledOnVersion( originalVersion) && feature_flags::gFeatureFlagLongCollectionNames.isEnabledOnVersion( @@ -494,6 +495,13 @@ private: abortAllReshardCollection(opCtx); } + // Create the pre-images collection if the feature flag is enabled on the requested version. + // TODO SERVER-61770: Remove once FCV 6.0 becomes last-lts. + if (feature_flags::gFeatureFlagChangeStreamPreAndPostImages.isEnabledOnVersion( + requestedVersion)) { + createChangeStreamPreImagesCollection(opCtx); + } + hangWhileUpgrading.pauseWhileSet(opCtx); } @@ -502,6 +510,9 @@ private: const SetFeatureCompatibilityVersion& request, boost::optional<Timestamp> changeTimestamp) { const auto requestedVersion = request.getCommandParameter(); + const bool preImagesFeatureFlagDisabledOnDowngradeVersion = + !feature_flags::gFeatureFlagChangeStreamPreAndPostImages.isEnabledOnVersion( + requestedVersion); if (serverGlobalParams.clusterRole == ClusterRole::ConfigServer) { // Tell the shards to enter phase-1 of setFCV @@ -537,6 +548,16 @@ private: dbName, MODE_X, [&](const CollectionPtr& collection) { + // Fail to downgrade if there exists a collection with + // 'changeStreamPreAndPostImages' enabled. + // TODO SERVER-61770: Remove once FCV 6.0 becomes last-lts. + uassert(ErrorCodes::CannotDowngrade, + str::stream() << "Cannot downgrade the cluster as collection " + << collection->ns() + << " has 'changeStreamPreAndPostImages' enabled", + preImagesFeatureFlagDisabledOnDowngradeVersion && + !collection->isChangeStreamPreAndPostImagesEnabled()); + invariant(collection->getTimeseriesOptions()); auto indexCatalog = collection->getIndexCatalog(); @@ -608,9 +629,30 @@ private: return true; }, [&](const CollectionPtr& collection) { - return collection->getTimeseriesOptions() != boost::none; + // TODO SERVER-61770: Remove 'changeStreamPreAndPostImages' check once + // FCV 6.0 becomes last-lts. + return collection->getTimeseriesOptions() != boost::none || + (preImagesFeatureFlagDisabledOnDowngradeVersion && + collection->isChangeStreamPreAndPostImagesEnabled()); }); } + + // Drop the pre-images collection if 'changeStreamPreAndPostImages' feature flag is not + // enabled on the downgrade version. + // TODO SERVER-61770: Remove once FCV 6.0 becomes last-lts. + if (preImagesFeatureFlagDisabledOnDowngradeVersion) { + DropReply dropReply; + const auto deletionStatus = + dropCollection(opCtx, + NamespaceString::kChangeStreamPreImagesNamespace, + &dropReply, + DropCollectionSystemCollectionMode::kAllowSystemCollectionDrops); + uassert(6023700, + str::stream() << "Failed to drop the change stream pre-images collection" + << causedBy(deletionStatus.reason()), + deletionStatus.isOK() || + deletionStatus.code() == ErrorCodes::NamespaceNotFound); + } } uassert(ErrorCodes::Error(549181), diff --git a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp index fdbcc43bb78..b3177b71eba 100644 --- a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp @@ -42,6 +42,7 @@ #include "mongo/bson/util/bson_extract.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/catalog/coll_mod.h" +#include "mongo/db/catalog/create_collection.h" #include "mongo/db/catalog/database.h" #include "mongo/db/catalog/database_holder.h" #include "mongo/db/catalog/local_oplog_info.h" @@ -60,6 +61,7 @@ #include "mongo/db/kill_sessions_local.h" #include "mongo/db/logical_time_validator.h" #include "mongo/db/op_observer.h" +#include "mongo/db/query/query_feature_flags_gen.h" #include "mongo/db/repl/always_allow_non_local_writes.h" #include "mongo/db/repl/bgsync.h" #include "mongo/db/repl/drop_pending_collection_reaper.h" @@ -582,6 +584,12 @@ OpTime ReplicationCoordinatorExternalStateImpl::onTransitionToPrimary(OperationC } }); + // Create the pre-images collection if it doesn't exist yet. + if (::mongo::feature_flags::gFeatureFlagChangeStreamPreAndPostImages.isEnabled( + serverGlobalParams.featureCompatibility)) { + createChangeStreamPreImagesCollection(opCtx); + } + serverGlobalParams.validateFeaturesAsPrimary.store(true); return opTimeToReturn; |