diff options
author | Max Hirschhorn <max.hirschhorn@mongodb.com> | 2017-04-19 17:09:07 -0400 |
---|---|---|
committer | Max Hirschhorn <max.hirschhorn@mongodb.com> | 2017-04-19 17:09:07 -0400 |
commit | a08c37500816cae71a366570d8caebf157887f63 (patch) | |
tree | 9f320285e5262a5588b5c79e11591370600af163 | |
parent | 71e41bc4935c8225ba14413900fcd69394b674da (diff) | |
download | mongo-a08c37500816cae71a366570d8caebf157887f63.tar.gz |
SERVER-26741 Retain temp collections when node restarted w/o --replSet.
Fixes an issue where temporary collections would be dropped after a
replica set member was restarted as a stand-alone mongod.
(cherry picked from commit 43e9c303c08f73b56490b662b3d11988cafafdf4)
-rw-r--r-- | jstests/replsets/temp_namespace_restart_as_standalone.js | 87 | ||||
-rw-r--r-- | src/mongo/db/db.cpp | 13 |
2 files changed, 96 insertions, 4 deletions
diff --git a/jstests/replsets/temp_namespace_restart_as_standalone.js b/jstests/replsets/temp_namespace_restart_as_standalone.js new file mode 100644 index 00000000000..063b1f3bbf4 --- /dev/null +++ b/jstests/replsets/temp_namespace_restart_as_standalone.js @@ -0,0 +1,87 @@ +/** + * Tests that temporary collections are not dropped when a member of a replica set is started up as + * a stand-alone mongod, i.e. without the --replSet parameter. + * + * @tags: [requires_persistence] + */ +(function() { + var rst = new ReplSetTest({nodes: 2}); + rst.startSet(); + + // Rig the election so that the first node becomes the primary and remains primary despite the + // secondary being terminated during this test. + var replSetConfig = rst.getReplSetConfig(); + replSetConfig.members[1].priority = 0; + replSetConfig.members[1].votes = 0; + rst.initiate(replSetConfig); + + var primaryConn = rst.getPrimary(); + var secondaryConn = rst.getSecondary(); + + var primaryDB = primaryConn.getDB("test"); + var secondaryDB = secondaryConn.getDB("test"); + + // Create a temporary collection and wait until the operation has replicated to the secondary. + assert.commandWorked(primaryDB.runCommand({ + create: "temp_collection", + temp: true, + })); + + // Verify that the temporary collection exists on the primary and has temp=true. + var primaryCollectionInfos = primaryDB.getCollectionInfos({name: "temp_collection"}); + assert.eq(1, primaryCollectionInfos.length, "'temp_collection' wasn't created on the primary"); + assert.eq("temp_collection", + primaryCollectionInfos[0].name, + "'temp_collection' wasn't created on the primary"); + assert.eq(true, + primaryCollectionInfos[0].options.temp, + "'temp_collection' wasn't created as temporary on the primary: " + + tojson(primaryCollectionInfos[0].options)); + + rst.awaitReplication(); + + // Verify that the temporary collection exists on the secondary and has temp=true. + var secondaryCollectionInfos = secondaryDB.getCollectionInfos({name: "temp_collection"}); + assert.eq( + 1, secondaryCollectionInfos.length, "'temp_collection' wasn't created on the secondary"); + assert.eq("temp_collection", + secondaryCollectionInfos[0].name, + "'temp_collection' wasn't created on the secondary"); + assert.eq(true, + secondaryCollectionInfos[0].options.temp, + "'temp_collection' wasn't created as temporary on the secondary: " + + tojson(secondaryCollectionInfos[0].options)); + + // Shut down the secondary and restart it as a stand-alone mongod. + var secondaryNodeId = rst.getNodeId(secondaryDB.getMongo()); + rst.stop(secondaryNodeId); + + secondaryConn = MongoRunner.runMongod({dbpath: secondaryConn.dbpath, noCleanData: true}); + assert.neq(null, secondaryConn, "secondary failed to start up as a stand-alone mongod"); + secondaryDB = secondaryConn.getDB("test"); + + // Verify that the temporary collection still exists on the secondary and has temp=true. + secondaryCollectionInfos = secondaryDB.getCollectionInfos({name: "temp_collection"}); + assert.eq(1, + secondaryCollectionInfos.length, + "'temp_collection' was dropped after restarting the secondary as a stand-alone"); + assert.eq("temp_collection", + secondaryCollectionInfos[0].name, + "'temp_collection' was dropped after restarting the secondary as a stand-alone"); + assert.eq(true, + secondaryCollectionInfos[0].options.temp, + "'temp_collection' is no longer temporary after restarting the secondary as a" + + " stand-alone: " + tojson(secondaryCollectionInfos[0].options)); + + // Shut down the secondary and restart it as a member of the replica set. + MongoRunner.stopMongod(secondaryConn); + + var restart = true; + rst.start(secondaryNodeId, {}, restart); + + // Verify that writes are replicated to the temporary collection and can successfully be applied + // by the secondary after having restarted it. + assert.writeOK(primaryDB.temp_collection.insert({}, {writeConcern: {w: 2, wtimeout: 60000}})); + + rst.stopSet(); +})(); diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index f5346915216..6b0dda65150 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -152,6 +152,7 @@ extern int diagLogging; namespace { const NamespaceString startupLogCollectionName("local.startup_log"); +const NamespaceString kSystemReplSetCollection("local.system.replset"); #ifdef _WIN32 ntservice::NtServiceDefaultStrings defaultServiceStrings = { @@ -296,12 +297,9 @@ void checkForIdIndexes(OperationContext* txn, Database* db) { * --replset. */ unsigned long long checkIfReplMissingFromCommandLine(OperationContext* txn) { - // This is helpful for the query below to work as you can't open files when readlocked - ScopedTransaction transaction(txn, MODE_X); - Lock::GlobalWrite lk(txn->lockState()); if (!repl::getGlobalReplicationCoordinator()->getSettings().usingReplSets()) { DBDirectClient c(txn); - return c.count("local.system.replset"); + return c.count(kSystemReplSetCollection.ns()); } return 0; } @@ -414,6 +412,13 @@ void repairDatabasesAndCheckVersion(OperationContext* txn) { const repl::ReplSettings& replSettings = repl::getGlobalReplicationCoordinator()->getSettings(); + // We open the "local" database before calling checkIfReplMissingFromCommandLine() to ensure the + // in-memory catalog entries for the 'kSystemReplSetCollection' collection have been populated + // if the collection exists. If the "local" database didn't exist at this point yet, then it + // will be created. + Lock::DBLock dbLock(txn->lockState(), kSystemReplSetCollection.db(), MODE_X); + dbHolder().openDb(txn, kSystemReplSetCollection.db()); + // On replica set members we only clear temp collections on DBs other than "local" during // promotion to primary. On pure slaves, they are only cleared when the oplog tells them // to. The local DB is special because it is not replicated. See SERVER-10927 for more |