From 6377d98fadd8ab5c10eb1e0bb240dc4151fda316 Mon Sep 17 00:00:00 2001 From: Eric Milkie Date: Wed, 15 Jul 2020 11:04:04 -0400 Subject: SERVER-47714 explicitly set the prepare conflict behavior prior to creating a system.profile collection --- jstests/replsets/profile.js | 30 ++++++++++++++++++++++++++++++ src/mongo/db/introspect.cpp | 11 +++++++++++ 2 files changed, 41 insertions(+) create mode 100644 jstests/replsets/profile.js diff --git a/jstests/replsets/profile.js b/jstests/replsets/profile.js new file mode 100644 index 00000000000..06c5a5d8815 --- /dev/null +++ b/jstests/replsets/profile.js @@ -0,0 +1,30 @@ +// Confirm that implicitly created profile collections are successful and do not trigger assertions. +// In order to implicitly create a profile collection with a read, we must set up the server with +// some data to read without the profiler being active. +// @tags: [requires_persistence] +(function() { +"use strict"; +let rst = new ReplSetTest({nodes: {n0: {profile: "0"}}}); +rst.startSet(); +rst.initiate(); +let primary = rst.getPrimary(); +let primaryDB = primary.getDB('test'); + +assert.commandWorked(primaryDB.foo.insert({_id: 1})); + +const nodeId = rst.getNodeId(primary); +rst.stop(nodeId); +rst.start(nodeId, {profile: "2"}, true /* preserves data directory */); +rst.awaitReplication(); +primary = rst.getPrimary(); +primaryDB = primary.getDB('test'); + +let oldAssertCounts = primaryDB.serverStatus().asserts; +assert.eq(0, primaryDB.system.profile.count()); +assert.eq([{_id: 1}], primaryDB.foo.aggregate([]).toArray()); +let newAssertCounts = primaryDB.serverStatus().asserts; +assert.eq(oldAssertCounts, newAssertCounts); +assert.eq(1, primaryDB.system.profile.count()); + +rst.stopSet(); +})(); diff --git a/src/mongo/db/introspect.cpp b/src/mongo/db/introspect.cpp index ab0b749c51e..a75b91b8aba 100644 --- a/src/mongo/db/introspect.cpp +++ b/src/mongo/db/introspect.cpp @@ -139,6 +139,17 @@ void profile(OperationContext* opCtx, NetworkOp op) { autoGetDb.reset(new AutoGetDb(opCtx, dbName, MODE_X)); if (autoGetDb->getDb()) { + // We are about to enforce prepare conflicts for the OperationContext. But it is + // illegal to change the behavior of ignoring prepare conflicts while any + // storage transaction is still active. So we need to call abandonSnapshot() to + // close any open transactions. This call is also harmless because any previous + // reads or writes should have already completed, as profile() is called at the + // end of an operation. + opCtx->recoveryUnit()->abandonSnapshot(); + // The profiler performs writes even after read commands. Ignoring prepare + // conflicts is not allowed while performing writes, so temporarily enforce + // prepare conflicts. + EnforcePrepareConflictsBlock enforcePrepare(opCtx); createProfileCollection(opCtx, autoGetDb->getDb()).transitional_ignore(); } } else { -- cgit v1.2.1