summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBlake Oler <blake.oler@mongodb.com>2018-11-01 12:14:56 -0400
committerBlake Oler <blake.oler@mongodb.com>2019-02-02 11:51:08 -0500
commit2b23402c019e05b98347dbb5760f51a4bb43d7d7 (patch)
treeaecf3d8642ac5636f395ce7ab51e6ed5e4c178c0
parentd0296d551c0dc079c6d57c2154b69d4dbe92a1e1 (diff)
downloadmongo-2b23402c019e05b98347dbb5760f51a4bb43d7d7.tar.gz
SERVER-37624 Allow sessions collection TTL index expiration value to change upon node restart
(cherry picked from commit f13d685ade22d662070bde942eb094790d4e7b8d)
-rw-r--r--jstests/libs/sessions_collection.js6
-rw-r--r--jstests/multiVersion/causal_consistency_downgrade_cluster.js10
-rw-r--r--jstests/multiVersion/causal_consistency_upgrade_cluster.js6
-rw-r--r--jstests/noPassthrough/sessions_collection_auto_healing.js34
-rw-r--r--jstests/replsets/sessions_collection_auto_healing.js61
-rw-r--r--src/mongo/db/sessions_collection.cpp16
-rw-r--r--src/mongo/db/sessions_collection.h5
-rw-r--r--src/mongo/db/sessions_collection_rs.cpp27
-rw-r--r--src/mongo/db/sessions_collection_standalone.cpp25
9 files changed, 151 insertions, 39 deletions
diff --git a/jstests/libs/sessions_collection.js b/jstests/libs/sessions_collection.js
index ba6d9b684c4..71ee9528d89 100644
--- a/jstests/libs/sessions_collection.js
+++ b/jstests/libs/sessions_collection.js
@@ -4,7 +4,8 @@
* Validates that the sessions collection exists if we expect it to,
* and has a TTL index on the lastUse field, if we expect it to.
*/
-function validateSessionsCollection(conn, collectionExists, indexExists, assertIfNotExists = true) {
+function validateSessionsCollection(
+ conn, collectionExists, indexExists, timeout, assertIfNotExists = true) {
var config = conn.getDB("config");
var info = config.getCollectionInfos({name: "system.sessions"});
@@ -26,6 +27,9 @@ function validateSessionsCollection(conn, collectionExists, indexExists, assertI
assert.eq(entry["ns"], "config.system.sessions");
assert.eq(entry["key"], {"lastUse": 1});
assert(entry.hasOwnProperty("expireAfterSeconds"));
+ if (timeout) {
+ assert.eq(entry["expireAfterSeconds"], timeout * 60);
+ }
}
}
diff --git a/jstests/multiVersion/causal_consistency_downgrade_cluster.js b/jstests/multiVersion/causal_consistency_downgrade_cluster.js
index e45dba25306..8571b16352e 100644
--- a/jstests/multiVersion/causal_consistency_downgrade_cluster.js
+++ b/jstests/multiVersion/causal_consistency_downgrade_cluster.js
@@ -55,10 +55,10 @@
// force config server to create sessions collection
assert.commandWorked(
st.configRS.getPrimary().getDB('admin').runCommand({refreshLogicalSessionCacheNow: 1}));
- validateSessionsCollection(st.configRS.getPrimary(), false, false, true);
+ validateSessionsCollection(st.configRS.getPrimary(), false, false, null, true);
// initially system.sessions collection has just one chunk.
- assert(validateSessionsCollection(st.rs0.getPrimary(), true, false, false) ||
- validateSessionsCollection(st.rs1.getPrimary(), true, false, false));
+ assert(validateSessionsCollection(st.rs0.getPrimary(), true, false, null, false) ||
+ validateSessionsCollection(st.rs1.getPrimary(), true, false, null, false));
// Change featureCompatibilityVersion to 3.4.
assert.commandWorked(st.s.adminCommand({setFeatureCompatibilityVersion: "3.4"}));
@@ -89,8 +89,8 @@
});
// Confirm that the system.sessions was dropped on the downgrade.
- validateSessionsCollection(st.rs0.getPrimary(), false, false, true);
- validateSessionsCollection(st.rs1.getPrimary(), false, false, true);
+ validateSessionsCollection(st.rs0.getPrimary(), false, false, null, true);
+ validateSessionsCollection(st.rs1.getPrimary(), false, false, null, true);
// Downgrade mongos first.
jsTest.log("Downgrading mongos servers.");
diff --git a/jstests/multiVersion/causal_consistency_upgrade_cluster.js b/jstests/multiVersion/causal_consistency_upgrade_cluster.js
index 39c8c812554..b3f871c1b9d 100644
--- a/jstests/multiVersion/causal_consistency_upgrade_cluster.js
+++ b/jstests/multiVersion/causal_consistency_upgrade_cluster.js
@@ -163,10 +163,10 @@
assert.commandWorked(
st.configRS.getPrimary().getDB('admin').runCommand({refreshLogicalSessionCacheNow: 1}));
// system.sessions collection can never be on config server.
- validateSessionsCollection(st.configRS.getPrimary(), false, false, true);
+ validateSessionsCollection(st.configRS.getPrimary(), false, false, null, true);
// The system sessions collection should have been created on some shard.
- assert(validateSessionsCollection(st.rs0.getPrimary(), true, true, false) ||
- validateSessionsCollection(st.rs1.getPrimary(), true, true, false));
+ assert(validateSessionsCollection(st.rs0.getPrimary(), true, true, null, false) ||
+ validateSessionsCollection(st.rs1.getPrimary(), true, true, null, false));
st.stop();
})();
diff --git a/jstests/noPassthrough/sessions_collection_auto_healing.js b/jstests/noPassthrough/sessions_collection_auto_healing.js
index 21eb3d221a3..9de17041fce 100644
--- a/jstests/noPassthrough/sessions_collection_auto_healing.js
+++ b/jstests/noPassthrough/sessions_collection_auto_healing.js
@@ -7,32 +7,54 @@ load('jstests/libs/sessions_collection.js');
// implicit sessions.
TestData.disableImplicitSessions = true;
+ let timeoutMinutes = 5;
+
var startSession = {startSession: 1};
- var conn = MongoRunner.runMongod({nojournal: ""});
+ var conn = MongoRunner.runMongod(
+ {nojournal: "", setParameter: "localLogicalSessionTimeoutMinutes=" + timeoutMinutes});
var admin = conn.getDB("admin");
var config = conn.getDB("config");
// Test that we can use sessions before the sessions collection exists.
{
- validateSessionsCollection(conn, false, false);
+ validateSessionsCollection(conn, false, false, timeoutMinutes);
assert.commandWorked(admin.runCommand({startSession: 1}));
- validateSessionsCollection(conn, false, false);
+ validateSessionsCollection(conn, false, false, timeoutMinutes);
}
// Test that a refresh will create the sessions collection.
{
assert.commandWorked(admin.runCommand({refreshLogicalSessionCacheNow: 1}));
- validateSessionsCollection(conn, true, true);
+ validateSessionsCollection(conn, true, true, timeoutMinutes);
}
// Test that a refresh will (re)create the TTL index on the sessions collection.
{
assert.commandWorked(config.system.sessions.dropIndex({lastUse: 1}));
- validateSessionsCollection(conn, true, false);
+ validateSessionsCollection(conn, true, false, timeoutMinutes);
+ assert.commandWorked(admin.runCommand({refreshLogicalSessionCacheNow: 1}));
+ validateSessionsCollection(conn, true, true, timeoutMinutes);
+ }
+
+ MongoRunner.stopMongod(conn);
+
+ timeoutMinutes = 4;
+ conn = MongoRunner.runMongod({
+ restart: conn,
+ cleanData: false,
+ setParameter: "localLogicalSessionTimeoutMinutes=" + timeoutMinutes
+ });
+ admin = conn.getDB("admin");
+ config = conn.getDB("config");
+
+ // Test that a change to the TTL index expiration on restart will generate a collMod to change
+ // the expiration time.
+ {
assert.commandWorked(admin.runCommand({refreshLogicalSessionCacheNow: 1}));
- validateSessionsCollection(conn, true, true);
+ validateSessionsCollection(conn, true, true, timeoutMinutes);
}
MongoRunner.stopMongod(conn);
+
})();
diff --git a/jstests/replsets/sessions_collection_auto_healing.js b/jstests/replsets/sessions_collection_auto_healing.js
index ed332241921..b75ed876d25 100644
--- a/jstests/replsets/sessions_collection_auto_healing.js
+++ b/jstests/replsets/sessions_collection_auto_healing.js
@@ -21,75 +21,102 @@ load('jstests/libs/sessions_collection.js');
var secondary = replTest.getSecondary();
var secondaryAdmin = secondary.getDB("admin");
+ // Get the current value of the TTL index so that we can verify it's being properly applied.
+ let res = assert.commandWorked(
+ primary.adminCommand({getParameter: 1, localLogicalSessionTimeoutMinutes: 1}));
+ let timeoutMinutes = res.localLogicalSessionTimeoutMinutes;
+
// Test that we can use sessions on the primary before the sessions collection exists.
{
- validateSessionsCollection(primary, false, false);
+ validateSessionsCollection(primary, false, false, timeoutMinutes);
assert.commandWorked(primaryAdmin.runCommand({startSession: 1}));
- validateSessionsCollection(primary, false, false);
+ validateSessionsCollection(primary, false, false, timeoutMinutes);
}
// Test that we can use sessions on secondaries before the sessions collection exists.
{
- validateSessionsCollection(primary, false, false);
+ validateSessionsCollection(primary, false, false, timeoutMinutes);
replTest.awaitReplication();
- validateSessionsCollection(secondary, false, false);
+ validateSessionsCollection(secondary, false, false, timeoutMinutes);
assert.commandWorked(secondaryAdmin.runCommand({startSession: 1}));
- validateSessionsCollection(primary, false, false);
+ validateSessionsCollection(primary, false, false, timeoutMinutes);
replTest.awaitReplication();
- validateSessionsCollection(secondary, false, false);
+ validateSessionsCollection(secondary, false, false, timeoutMinutes);
}
// Test that a refresh on a secondary does not create the sessions collection.
{
- validateSessionsCollection(primary, false, false);
+ validateSessionsCollection(primary, false, false, timeoutMinutes);
replTest.awaitReplication();
- validateSessionsCollection(secondary, false, false);
+ validateSessionsCollection(secondary, false, false, timeoutMinutes);
assert.commandWorked(secondaryAdmin.runCommand({refreshLogicalSessionCacheNow: 1}));
- validateSessionsCollection(primary, false, false);
+ validateSessionsCollection(primary, false, false, timeoutMinutes);
replTest.awaitReplication();
- validateSessionsCollection(secondary, false, false);
+ validateSessionsCollection(secondary, false, false, timeoutMinutes);
}
// Test that a refresh on the primary creates the sessions collection.
{
- validateSessionsCollection(primary, false, false);
+ validateSessionsCollection(primary, false, false, timeoutMinutes);
replTest.awaitReplication();
- validateSessionsCollection(secondary, false, false);
+ validateSessionsCollection(secondary, false, false, timeoutMinutes);
assert.commandWorked(primaryAdmin.runCommand({refreshLogicalSessionCacheNow: 1}));
- validateSessionsCollection(primary, true, true);
+ validateSessionsCollection(primary, true, true, timeoutMinutes);
}
// Test that a refresh on a secondary will not create the TTL index on the sessions collection.
{
assert.commandWorked(primary.getDB("config").system.sessions.dropIndex({lastUse: 1}));
- validateSessionsCollection(primary, true, false);
+ validateSessionsCollection(primary, true, false, timeoutMinutes);
assert.commandWorked(secondaryAdmin.runCommand({refreshLogicalSessionCacheNow: 1}));
- validateSessionsCollection(primary, true, false);
+ validateSessionsCollection(primary, true, false, timeoutMinutes);
}
// Test that a refresh on the primary will create the TTL index on the sessions collection.
{
- validateSessionsCollection(primary, true, false);
+ validateSessionsCollection(primary, true, false, timeoutMinutes);
+
+ assert.commandWorked(primaryAdmin.runCommand({refreshLogicalSessionCacheNow: 1}));
+
+ validateSessionsCollection(primary, true, true, timeoutMinutes);
+ }
+
+ timeoutMinutes = 4;
+ replTest.restart(
+ 0,
+ {startClean: false, setParameter: "localLogicalSessionTimeoutMinutes=" + timeoutMinutes});
+
+ primary = replTest.getPrimary();
+ primaryAdmin = primary.getDB("admin");
+ secondary = replTest.getSecondary();
+
+ // Test that a change to the TTL index expiration on restart will generate a collMod to change
+ // the expiration time.
+ {
assert.commandWorked(primaryAdmin.runCommand({refreshLogicalSessionCacheNow: 1}));
- validateSessionsCollection(primary, true, true);
+ validateSessionsCollection(primary, true, true, timeoutMinutes);
+
+ replTest.awaitReplication();
+ validateSessionsCollection(secondary, true, true, timeoutMinutes);
}
replTest.stopSet();
+
})();
diff --git a/src/mongo/db/sessions_collection.cpp b/src/mongo/db/sessions_collection.cpp
index 05b6df1666c..e4bad86e337 100644
--- a/src/mongo/db/sessions_collection.cpp
+++ b/src/mongo/db/sessions_collection.cpp
@@ -318,4 +318,20 @@ BSONObj SessionsCollection::generateCreateIndexesCmd() {
return createIndexes.toBSON();
}
+
+BSONObj SessionsCollection::generateCollModCmd() {
+ BSONObjBuilder collModCmdBuilder;
+
+ collModCmdBuilder << "collMod" << kSessionsNamespaceString.coll();
+
+ BSONObjBuilder indexBuilder(collModCmdBuilder.subobjStart("index"));
+ indexBuilder << "name" << kSessionsTTLIndex;
+ indexBuilder << "expireAfterSeconds" << localLogicalSessionTimeoutMinutes * 60;
+
+ indexBuilder.done();
+ collModCmdBuilder.done();
+
+ return collModCmdBuilder.obj();
+}
+
} // namespace mongo
diff --git a/src/mongo/db/sessions_collection.h b/src/mongo/db/sessions_collection.h
index e592e9a7b5d..5cb0c595419 100644
--- a/src/mongo/db/sessions_collection.h
+++ b/src/mongo/db/sessions_collection.h
@@ -97,6 +97,11 @@ public:
*/
static BSONObj generateCreateIndexesCmd();
+ /*
+ * Generates a collMod command for the sessions collection TTL index.
+ */
+ static BSONObj generateCollModCmd();
+
protected:
/**
* Makes a send function for the given client.
diff --git a/src/mongo/db/sessions_collection_rs.cpp b/src/mongo/db/sessions_collection_rs.cpp
index e3c729ca61c..373f1618eaf 100644
--- a/src/mongo/db/sessions_collection_rs.cpp
+++ b/src/mongo/db/sessions_collection_rs.cpp
@@ -161,10 +161,22 @@ Status SessionsCollectionRS::setupSessionsCollection(OperationContext* opCtx) {
kSessionsNamespaceString,
opCtx,
[&] {
- // Creating the TTL index will auto-generate the collection.
+ auto existsStatus = checkSessionsCollectionExists(opCtx);
+ if (existsStatus.isOK()) {
+ return Status::OK();
+ }
+
DBDirectClient client(opCtx);
+ BSONObj cmd;
+
+ if (existsStatus.code() == ErrorCodes::IndexOptionsConflict) {
+ cmd = generateCollModCmd();
+ } else {
+ // Creating the TTL index will auto-generate the collection.
+ cmd = generateCreateIndexesCmd();
+ }
+
BSONObj info;
- auto cmd = generateCreateIndexesCmd();
if (!client.runCommand(kSessionsNamespaceString.db().toString(), cmd, info)) {
return getStatusFromCommandResult(info);
}
@@ -183,15 +195,22 @@ Status SessionsCollectionRS::checkSessionsCollectionExists(OperationContext* opC
return Status{ErrorCodes::NamespaceNotFound, "config.system.sessions does not exist"};
}
- auto indexExists = std::find_if(indexes.begin(), indexes.end(), [](const BSONObj& index) {
+ auto index = std::find_if(indexes.begin(), indexes.end(), [](const BSONObj& index) {
return index.getField("name").String() == kSessionsTTLIndex;
});
- if (indexExists == indexes.end()) {
+ if (index == indexes.end()) {
return Status{ErrorCodes::IndexNotFound,
"config.system.sessions does not have the required TTL index"};
}
+ if (!index->hasField("expireAfterSeconds") ||
+ index->getField("expireAfterSeconds").Int() != (localLogicalSessionTimeoutMinutes * 60)) {
+ return Status{
+ ErrorCodes::IndexOptionsConflict,
+ "config.system.sessions currently has the incorrect timeout for the TTL index"};
+ }
+
return Status::OK();
}
diff --git a/src/mongo/db/sessions_collection_standalone.cpp b/src/mongo/db/sessions_collection_standalone.cpp
index b8a39186c60..e5ad672b485 100644
--- a/src/mongo/db/sessions_collection_standalone.cpp
+++ b/src/mongo/db/sessions_collection_standalone.cpp
@@ -55,8 +55,20 @@ Status SessionsCollectionStandalone::setupSessionsCollection(OperationContext* o
return {ErrorCodes::MustUpgrade, "Can not create config.system.sessions collection"};
}
+ auto existsStatus = checkSessionsCollectionExists(opCtx);
+ if (existsStatus.isOK()) {
+ return Status::OK();
+ }
+
DBDirectClient client(opCtx);
- auto cmd = generateCreateIndexesCmd();
+ BSONObj cmd;
+
+ if (existsStatus.code() == ErrorCodes::IndexOptionsConflict) {
+ cmd = generateCollModCmd();
+ } else {
+ cmd = generateCreateIndexesCmd();
+ }
+
BSONObj info;
if (!client.runCommand(kSessionsNamespaceString.db().toString(), cmd, info)) {
return getStatusFromCommandResult(info);
@@ -74,15 +86,22 @@ Status SessionsCollectionStandalone::checkSessionsCollectionExists(OperationCont
return Status{ErrorCodes::NamespaceNotFound, "config.system.sessions does not exist"};
}
- auto indexExists = std::find_if(indexes.begin(), indexes.end(), [](const BSONObj& index) {
+ auto index = std::find_if(indexes.begin(), indexes.end(), [](const BSONObj& index) {
return index.getField("name").String() == kSessionsTTLIndex;
});
- if (indexExists == indexes.end()) {
+ if (index == indexes.end()) {
return Status{ErrorCodes::IndexNotFound,
"config.system.sessions does not have the required TTL index"};
};
+ if (!index->hasField("expireAfterSeconds") ||
+ index->getField("expireAfterSeconds").Int() != (localLogicalSessionTimeoutMinutes * 60)) {
+ return Status{
+ ErrorCodes::IndexOptionsConflict,
+ "config.system.sessions currently has the incorrect timeout for the TTL index"};
+ }
+
return Status::OK();
}