summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSpencer Jackson <spencer.jackson@mongodb.com>2022-03-15 21:07:25 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-03-15 22:00:37 +0000
commit054160f7e2232f398ec23e90fb83aa81cd2861d0 (patch)
treeddf5f005965069953b84fd3d9957ed5b3d9735bf
parent6076dff1727970e6f94d1d89f6e3a0b2eefd381c (diff)
downloadmongo-054160f7e2232f398ec23e90fb83aa81cd2861d0.tar.gz
SERVER-63708 Ensure that TTL indexes work correctly with user write blocking
-rw-r--r--jstests/noPassthrough/user_write_blocking_ttl_index.js100
-rw-r--r--src/mongo/db/ttl.cpp3
2 files changed, 103 insertions, 0 deletions
diff --git a/jstests/noPassthrough/user_write_blocking_ttl_index.js b/jstests/noPassthrough/user_write_blocking_ttl_index.js
new file mode 100644
index 00000000000..e7a11bffd3f
--- /dev/null
+++ b/jstests/noPassthrough/user_write_blocking_ttl_index.js
@@ -0,0 +1,100 @@
+// Validate that write blocking mode prevents reaping of user, but not system, TTL indexes
+// @tags: [
+// creates_and_authenticates_user,
+// requires_auth,
+// requires_fcv_60,
+// requires_non_retryable_commands,
+// requires_replication,
+// featureFlagUserWriteBlocking,
+// ]
+
+(function() {
+"use strict";
+
+load("jstests/libs/fail_point_util.js");
+
+// Test on replset primary
+const rst = new ReplSetTest({
+ nodes: 1,
+ nodeOptions: {
+ setParameter: {
+ "ttlMonitorSleepSecs": 1,
+ }
+ }
+});
+rst.startSet();
+rst.initiate();
+const primary = rst.getPrimary();
+
+function runTest(conn, testCase) {
+ const admin = conn.getDB("admin");
+
+ // Make sure the TTLMonitor is disabled, while not holding any global locks
+ const pauseTtl = configureFailPoint(primary, 'hangTTLMonitorBetweenPasses');
+ pauseTtl.wait();
+
+ function runTTLMonitor() {
+ const ttlPass = admin.serverStatus().metrics.ttl.passes;
+ assert.commandWorked(
+ admin.runCommand({configureFailPoint: "hangTTLMonitorBetweenPasses", mode: {skip: 1}}));
+ assert.soon(() => admin.serverStatus().metrics.ttl.passes >= ttlPass + 1,
+ "TTL monitor didn't run before timing out.");
+ }
+
+ // Set up data and TTL indexes
+ for (const target of testCase) {
+ target.db = conn.getDB(target.dbname);
+ target.col = target.db.getCollection(target.colName);
+
+ target.col.insertOne({"createdAt": new Date(), "logEvent": 2, "logMessage": "Success!"});
+
+ assert.commandWorked(target.col.createIndex({"createdAt": 1}, {expireAfterSeconds: 0}));
+ }
+
+ // Enable global write block mode
+ assert.commandWorked(admin.runCommand({setUserWriteBlockMode: 1, global: true}));
+ for (const target of testCase) {
+ assert.eq(1, target.col.count());
+ }
+
+ // Run the TTLMonitor, and expect user collections to remain unreaped
+ runTTLMonitor();
+ for (const target of testCase) {
+ if (target.expectReap) {
+ assert.eq(0, target.col.count());
+ } else {
+ assert.eq(1, target.col.count());
+ checkLog.containsJson(conn, 5400703, {
+ "error":
+ {"code": 96, "codeName": "OperationFailed", "errmsg": "User writes blocked"}
+ });
+ }
+ }
+
+ // Disable write blocking, then run the TTLMonitor, expecting it to reap all collections
+ assert.commandWorked(admin.runCommand({setUserWriteBlockMode: 1, global: false}));
+ runTTLMonitor();
+
+ for (const target of testCase) {
+ // All documents should be reaped now
+ assert.soon(() => target.col.count() == 0);
+
+ // Finally, cleanup the collection
+ if (target.colName != "system.js") {
+ assert(target.col.drop());
+ }
+ }
+}
+
+runTest(primary, [
+ // Collections in a regular database should not be reaped
+ {dbname: "db", colName: "user_write_blocking_ttl_index", expectReap: false},
+ // Not even system collections in a regular database should be reaped
+ {dbname: "db2", colName: "system.js", expectReap: false},
+ // Collections in system databases should be reaped
+ {dbname: "admin", colName: "user_write_blocking_ttl_index", expectReap: true},
+ {dbname: "config", colName: "user_write_blocking_ttl_index", expectReap: true},
+ {dbname: "local", colName: "user_write_blocking_ttl_index", expectReap: true}
+]);
+rst.stopSet();
+})();
diff --git a/src/mongo/db/ttl.cpp b/src/mongo/db/ttl.cpp
index 1c91a5fc470..29510d67253 100644
--- a/src/mongo/db/ttl.cpp
+++ b/src/mongo/db/ttl.cpp
@@ -76,6 +76,7 @@ const auto getTTLMonitor = ServiceContext::declareDecoration<std::unique_ptr<TTL
} // namespace
MONGO_FAIL_POINT_DEFINE(hangTTLMonitorWithLock);
+MONGO_FAIL_POINT_DEFINE(hangTTLMonitorBetweenPasses);
Counter64 ttlPasses;
Counter64 ttlDeletedDocuments;
@@ -181,6 +182,8 @@ private:
const ServiceContext::UniqueOperationContext opCtxPtr = cc().makeOperationContext();
OperationContext* opCtx = opCtxPtr.get();
+ hangTTLMonitorBetweenPasses.pauseWhileSet(opCtx);
+
// If part of replSet but not in a readable state (e.g. during initial sync), skip.
if (repl::ReplicationCoordinator::get(opCtx)->getReplicationMode() ==
repl::ReplicationCoordinator::modeReplSet &&