diff options
author | Dianna Hohensee <dianna.hohensee@mongodb.com> | 2020-07-08 15:56:16 -0400 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-07-09 20:16:42 +0000 |
commit | bf3b9a1e6d52dddbb9ed85c113d613ffbc866e13 (patch) | |
tree | 25bbc1e3b100d08b01149a326befdb5b18a81774 | |
parent | 64b48df7ee020d6ac68aaeda4523bfccf0d5b7cd (diff) | |
download | mongo-bf3b9a1e6d52dddbb9ed85c113d613ffbc866e13.tar.gz |
SERVER-49366 Allow presence of 'recordPreImages' in collection metadata in standalone mode
4 files changed, 198 insertions, 74 deletions
diff --git a/jstests/noPassthrough/recordPreImages_in_create_and_collmod.js b/jstests/noPassthrough/recordPreImages_in_create_and_collmod.js new file mode 100644 index 00000000000..4807da95268 --- /dev/null +++ b/jstests/noPassthrough/recordPreImages_in_create_and_collmod.js @@ -0,0 +1,54 @@ +/* + * Tests the 'recordPreImage' flag is settable via the collMode and create commands. Also tests that + * this flag cannot be set on collections in the 'local' or 'admin' databases. + * + * @tags: [requires_replication] + */ +(function() { +'use strict'; + +const findCollectionInfo = function(collName) { + var all = testDB.getCollectionInfos(); + if (all.length == 0) { + return {}; + } + all = all.filter(function(z) { + return z.name == collName; + }); + assert.eq(all.length, 1); + return all[0]; +}; + +let rsTest = new ReplSetTest({nodes: 1}); +rsTest.startSet(); +rsTest.initiate(); + +const dbName = 'testDB'; +const collName = 'recordPreImageColl'; +const collName2 = 'recordPreImageColl2'; + +let primary = rsTest.getPrimary(); +let adminDB = primary.getDB("admin"); +let localDB = primary.getDB("local"); +let testDB = primary.getDB(dbName); + +// Check that we cannot set recordPreImages on the local or admin databases. +assert.commandFailedWithCode(adminDB.runCommand({create: collName, recordPreImages: true}), + ErrorCodes.InvalidOptions); +assert.commandFailedWithCode(localDB.runCommand({create: collName, recordPreImages: true}), + ErrorCodes.InvalidOptions); + +// We should be able to set the recordPreImages flag via create or collMod. +assert.commandWorked(testDB.runCommand({create: collName, recordPreImages: true})); +assert.eq(findCollectionInfo(collName).options.recordPreImages, true); + +assert.commandWorked(testDB.runCommand({create: collName2})); +assert.commandWorked(testDB.runCommand({collMod: collName2, recordPreImages: true})); +assert.eq(findCollectionInfo(collName2).options.recordPreImages, true); + +// Test that the recordPreImages flag can be unset successfully using the 'collMod' command. +assert.commandWorked(testDB.runCommand({collMod: collName, recordPreImages: false})); +assert.eq(findCollectionInfo(collName).options, {}); + +rsTest.stopSet(); +}()); diff --git a/jstests/noPassthrough/recordPreImages_standalone_mode.js b/jstests/noPassthrough/recordPreImages_standalone_mode.js new file mode 100644 index 00000000000..26d32c84e45 --- /dev/null +++ b/jstests/noPassthrough/recordPreImages_standalone_mode.js @@ -0,0 +1,144 @@ +/** + * Test that standalones are able to startup with 'recordPreImages' set in collection metadata; and + * that 'recordPreImages' is inactive in standalone mode. + * + * @tags: [ + * # Servers are restarted in this test and the data must be retained. + * requires_persistence, + * # This test uses a replica set and must avoid replica set incompatible test suites, like the + * # test suite that turns journaling off. + * requires_replication, + * ] + */ + +(function() { +'use strict'; + +/** + * Fetch the collection information on database 'nodeDB' for collection 'collName'. Expects the + * collection to exist. + */ +const findCollectionInfo = function(nodeDB, collName) { + let collInfos = nodeDB.getCollectionInfos(); + assert.gt(collInfos.length, 0, "The database is empty"); + + let collInfo = collInfos.filter(function(z) { + return z.name == collName; + }); + + assert.eq(collInfo.length, 1); + return collInfo[0]; +}; + +/** + * Prints out all of the oplog collection entries on 'node'. + */ +function printOplog(node) { + let cursor = node.getDB('local').oplog.rs.find(); + while (cursor.hasNext()) { + jsTest.log("Oplog entry: " + tojson(cursor.next())); + } +} + +/** + * A --replSet server should be able to set 'recordPreImages' on a collection and then be restarted + * in standalone mode successfully. + */ + +jsTest.log("Starting up a 1-node replica set"); + +var rst = new ReplSetTest({nodes: 1}); +rst.startSet(); +rst.initiate(); + +const dbName = 'record_preimage_standalone_mode_test_db'; +const collName = 'testColl'; +let primary = rst.getPrimary(); +let testDB = primary.getDB(dbName); +let testColl = testDB.getCollection(collName); + +jsTest.log("Creating a collection with 'recordPreImages' set to true and adding some data"); + +assert.commandWorked(testDB.runCommand({create: collName, recordPreImages: true})); +assert.eq(findCollectionInfo(testDB, collName).options.recordPreImages, true); + +assert.commandWorked(testColl.insert({a: 1, b: 1})); +assert.commandWorked(testColl.update({a: 1}, {a: 2, b: 2})); +// Ensure all of the writes make it to disk before checkpointing below. +assert.commandWorked(testColl.update({a: 2}, {a: 3, b: 3}, {writeConcern: {w: 1, j: true}})); + +jsTest.log("Forcing a checkpoint to be taken"); + +// Ensure that the standalone can recover all of the writes from the last checkpoint because +// standalone mode does not run recovery from the oplog. The last write with j:true write concern +// ensured that the data reached disk, and now fsync will checkpoint that data. +assert.commandWorked(primary.adminCommand({fsync: 1})); + +jsTest.log("Restarting the replica set member as a standalone node"); + +printOplog(primary); // Debugging aid. + +let replicaSetNodeId = rst.getNodeId(primary); +let replicaSetNodeDbpath = primary.dbpath; +jsTest.log("replicaSetNodeId: " + replicaSetNodeId + + ", replicaSetNodeDbpath: " + replicaSetNodeDbpath); + +rst.stop(replicaSetNodeId); + +let standaloneConn = MongoRunner.runMongod({ + dbpath: replicaSetNodeDbpath, + noCleanData: true, +}); + +let standaloneDB = standaloneConn.getDB(dbName); +let standaloneColl = standaloneDB.getCollection(collName); +let standaloneOplogColl = standaloneConn.getDB('local').oplog.rs; + +assert.eq(findCollectionInfo(standaloneDB, collName).options.recordPreImages, true); + +/** + * A standalone mode server should be able to do writes without triggering the 'recordPreImages' + * feature because oplog entries are not written in standalone mode: the 'recordPreImages' setting + * causes additional oplog entries to be written. + */ + +const numOplogEntriesBefore = standaloneOplogColl.find().itcount(); + +jsTest.log( + "Updating some data in the collection with 'recordPreImages' set to check that nothing " + + "happens in standalone mode"); + +assert.commandWorked(standaloneColl.insert({c: 1, d: 1})); +assert.commandWorked(standaloneColl.update({c: 1}, {c: 2, d: 2})); +assert.commandWorked(standaloneColl.update({c: 2}, {c: 3, d: 3})); + +jsTest.log( + "Checking that no oplog entries were produced for 'recordPreImages': the feature is inactive"); + +printOplog(standaloneConn); // Debugging aid. + +const numOplogEntriesAfter = standaloneOplogColl.find().itcount(); +assert.eq(numOplogEntriesBefore, numOplogEntriesAfter); + +jsTest.log("Shutting down standalone"); + +MongoRunner.stopMongod(standaloneConn); + +jsTest.log("Restarting the node as a replica set member again and doing some writes"); + +rst.start(replicaSetNodeId, {}, true /*restart*/); + +primary = rst.getPrimary(); +testDB = primary.getDB(dbName); +testColl = testDB.getCollection(collName); + +// Check that everything is still working properly after being in standalone mode. +assert.eq(findCollectionInfo(testDB, collName).options.recordPreImages, true); + +assert.commandWorked(testColl.update({a: 3}, {a: 4, b: 4})); +assert.commandWorked(testColl.update({a: 4}, {a: 5, b: 5})); + +jsTest.log("Shutting down replica set"); + +rst.stopSet(); +}()); diff --git a/jstests/noPassthrough/record_preimage_startup_validation.js b/jstests/noPassthrough/record_preimage_startup_validation.js deleted file mode 100644 index 3e9804d0303..00000000000 --- a/jstests/noPassthrough/record_preimage_startup_validation.js +++ /dev/null @@ -1,68 +0,0 @@ -/* - * This test validates that we accept the 'recordPreImage' flag via the collMode and create commands - * only if the node is a member of a replica set that is not a shard/config server. Also tests that - * this flag cannot be set on collections in the 'local' or 'admin' databases. - * - * @tags: [requires_replication, requires_persistence] - */ -(function() { -'use strict'; - -// Start a mongod that's not in a replica set -let conn = MongoRunner.runMongod({}); - -let testDB = conn.getDB("test"); - -const findCollectionInfo = function(collName) { - var all = testDB.getCollectionInfos(); - if (all.length == 0) { - return {}; - } - all = all.filter(function(z) { - return z.name == collName; - }); - assert.eq(all.length, 1); - return all[0]; -}; - -// Check that we cannot record pre-images on a standalone. -assert.commandFailedWithCode(testDB.runCommand({create: "test", recordPreImages: true}), - ErrorCodes.InvalidOptions); -// Check that failing to set the option doesn't accidentally set it anyways. -assert.eq(findCollectionInfo("test").options, undefined); - -MongoRunner.stopMongod(conn); - -// Start a 1-node repl set to be used for the rest of the test -let rsTest = new ReplSetTest({nodes: 1}); -rsTest.startSet(); -rsTest.initiate(); - -// Check that we cannot set recordPreImages on the local or admin databases. -let adminDB = rsTest.getPrimary().getDB("admin"); -assert.commandFailedWithCode(adminDB.runCommand({create: "preimagecoll", recordPreImages: true}), - ErrorCodes.InvalidOptions); -let localDB = rsTest.getPrimary().getDB("local"); -assert.commandFailedWithCode(localDB.runCommand({create: "preimagecoll", recordPreImages: true}), - ErrorCodes.InvalidOptions); - -testDB = rsTest.getPrimary().getDB("test"); - -// Check the positive test cases. We should be able to set this flag via create or collMod. -assert.commandWorked(testDB.runCommand({create: "test", recordPreImages: true})); -assert.eq(findCollectionInfo("test").options.recordPreImages, true); - -assert.commandWorked(testDB.runCommand({create: "test2"})); -assert.commandWorked(testDB.runCommand({collMod: "test2", recordPreImages: true})); -assert.eq(findCollectionInfo("test2").options.recordPreImages, true); - -// Test that the flag can be unset successfully using the 'collMod' command. -assert.commandWorked(testDB.runCommand({collMod: "test", recordPreImages: false})); -assert.eq(findCollectionInfo("test").options, {}); - -// Re-enable the flag and test that the replica set node can restart successfully. -assert.commandWorked(testDB.runCommand({collMod: "test", recordPreImages: true})); -rsTest.restart(rsTest.getPrimary()); - -rsTest.stopSet(); -}()); diff --git a/src/mongo/db/catalog/collection_impl.cpp b/src/mongo/db/catalog/collection_impl.cpp index 39a0ef9af1e..523edfe2718 100644 --- a/src/mongo/db/catalog/collection_impl.cpp +++ b/src/mongo/db/catalog/collection_impl.cpp @@ -230,12 +230,6 @@ Status validatePreImageRecording(OperationContext* opCtx, const NamespaceString& "recordPreImages collection option is not supported on shards or config servers"}; } - auto replCoord = repl::ReplicationCoordinator::get(opCtx); - if (!replCoord->isReplEnabled()) { - return {ErrorCodes::InvalidOptions, - "recordPreImages collection option depends on being in a replica set"}; - } - return Status::OK(); } |