summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorCheahuychou Mao <mao.cheahuychou@gmail.com>2021-07-30 07:59:06 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-08-14 19:06:10 +0000
commit507db599d04640d4585c6cf85ae9143fc1e628f2 (patch)
tree1697969e431b57b35a682a4e65b9dc31a364319b /src/mongo
parent632105d33ab6931e86626db08160800d41bb1329 (diff)
downloadmongo-507db599d04640d4585c6cf85ae9143fc1e628f2.tar.gz
SERVER-58751 Make logical session cache track related sessions as one logical session
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/db/SConscript14
-rw-r--r--src/mongo/db/initialize_operation_session_info.cpp6
-rw-r--r--src/mongo/db/internal_transactions_feature_flag.idl (renamed from src/mongo/s/internal_transactions_feature_flag.idl)0
-rw-r--r--src/mongo/db/logical_session_cache_impl.cpp25
-rw-r--r--src/mongo/db/logical_session_cache_test.cpp187
-rw-r--r--src/mongo/db/logical_session_id_helpers.cpp18
-rw-r--r--src/mongo/db/logical_session_id_test.cpp93
-rw-r--r--src/mongo/db/session_catalog.cpp8
-rw-r--r--src/mongo/db/session_catalog_test.cpp24
-rw-r--r--src/mongo/s/SConscript1
10 files changed, 330 insertions, 46 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index f6296a79e5c..294d0a376b0 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -642,6 +642,18 @@ env.Library(
)
env.Library(
+ target='internal_transactions_feature_flag',
+ source=[
+ 'internal_transactions_feature_flag.idl',
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/idl/feature_flag',
+ '$BUILD_DIR/mongo/idl/server_parameter',
+ 'server_options_core',
+ ]
+)
+
+env.Library(
target='session_catalog',
source=[
'session_catalog.cpp',
@@ -652,6 +664,7 @@ env.Library(
'logical_session_id_helpers',
],
LIBDEPS_PRIVATE=[
+ 'internal_transactions_feature_flag',
'service_context',
]
)
@@ -1517,6 +1530,7 @@ env.Library(
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/db/s/sharding_api_d',
+ 'internal_transactions_feature_flag',
]
)
diff --git a/src/mongo/db/initialize_operation_session_info.cpp b/src/mongo/db/initialize_operation_session_info.cpp
index afa0c739b6f..c655b9ae7f7 100644
--- a/src/mongo/db/initialize_operation_session_info.cpp
+++ b/src/mongo/db/initialize_operation_session_info.cpp
@@ -94,6 +94,12 @@ OperationSessionInfoFromClient initializeOperationSessionInfo(OperationContext*
return {};
}
+ if (getParentSessionId(lsid)) {
+ uassert(ErrorCodes::InvalidOptions,
+ "Internal sessions are not supported outside of transactions",
+ osi.getTxnNumber() && osi.getAutocommit() && !osi.getAutocommit().value());
+ }
+
opCtx->setLogicalSessionId(std::move(lsid));
uassertStatusOK(lsc->vivify(opCtx, opCtx->getLogicalSessionId().get()));
} else {
diff --git a/src/mongo/s/internal_transactions_feature_flag.idl b/src/mongo/db/internal_transactions_feature_flag.idl
index 83fe8b1a644..83fe8b1a644 100644
--- a/src/mongo/s/internal_transactions_feature_flag.idl
+++ b/src/mongo/db/internal_transactions_feature_flag.idl
diff --git a/src/mongo/db/logical_session_cache_impl.cpp b/src/mongo/db/logical_session_cache_impl.cpp
index 349da7906f9..52bee61ed64 100644
--- a/src/mongo/db/logical_session_cache_impl.cpp
+++ b/src/mongo/db/logical_session_cache_impl.cpp
@@ -33,6 +33,7 @@
#include "mongo/db/logical_session_cache_impl.h"
+#include "mongo/db/internal_transactions_feature_flag_gen.h"
#include "mongo/db/logical_session_id.h"
#include "mongo/db/logical_session_id_helpers.h"
#include "mongo/db/operation_context.h"
@@ -93,8 +94,17 @@ Status LogicalSessionCacheImpl::startSession(OperationContext* opCtx,
}
Status LogicalSessionCacheImpl::vivify(OperationContext* opCtx, const LogicalSessionId& lsid) {
+ auto parentLsid = getParentSessionId(lsid);
+ if (parentLsid) {
+ uassert(ErrorCodes::InvalidOptions,
+ "Internal transactions are not enabled",
+ feature_flags::gFeatureFlagInternalTransactions.isEnabled(
+ serverGlobalParams.featureCompatibility));
+ }
+
stdx::lock_guard lg(_mutex);
- auto it = _activeSessions.find(lsid);
+
+ auto it = _activeSessions.find(parentLsid ? *parentLsid : lsid);
if (it == _activeSessions.end())
return _addToCacheIfNotFull(lg, makeLogicalSessionRecord(opCtx, lsid, _service->now()));
@@ -374,6 +384,11 @@ void LogicalSessionCacheImpl::_refresh(Client* client) {
}
void LogicalSessionCacheImpl::endSessions(const LogicalSessionIdSet& sessions) {
+ for (const auto& lsid : sessions) {
+ uassert(ErrorCodes::InvalidOptions,
+ str::stream() << "Cannot specify a session id with parent session id " << lsid,
+ !getParentSessionId(lsid));
+ }
stdx::lock_guard<Latch> lk(_mutex);
_endingSessions.insert(begin(sessions), end(sessions));
}
@@ -435,9 +450,13 @@ std::vector<LogicalSessionId> LogicalSessionCacheImpl::listIds(
}
boost::optional<LogicalSessionRecord> LogicalSessionCacheImpl::peekCached(
- const LogicalSessionId& id) const {
+ const LogicalSessionId& lsid) const {
+ uassert(ErrorCodes::InvalidOptions,
+ str::stream() << "Cannot specify a session id with parent session id " << lsid,
+ !getParentSessionId(lsid));
+
stdx::lock_guard<Latch> lk(_mutex);
- const auto it = _activeSessions.find(id);
+ const auto it = _activeSessions.find(lsid);
if (it == _activeSessions.end()) {
return boost::none;
}
diff --git a/src/mongo/db/logical_session_cache_test.cpp b/src/mongo/db/logical_session_cache_test.cpp
index d1a6be816e1..9500f65f3d1 100644
--- a/src/mongo/db/logical_session_cache_test.cpp
+++ b/src/mongo/db/logical_session_cache_test.cpp
@@ -47,6 +47,7 @@
#include "mongo/db/service_context_test_fixture.h"
#include "mongo/db/service_liaison_mock.h"
#include "mongo/db/sessions_collection_mock.h"
+#include "mongo/idl/server_parameter_test_util.h"
#include "mongo/stdx/future.h"
#include "mongo/unittest/ensure_fcv.h"
#include "mongo/unittest/unittest.h"
@@ -118,68 +119,172 @@ private:
std::shared_ptr<MockSessionsCollectionImpl> _sessions;
std::unique_ptr<LogicalSessionCache> _cache;
+
+ RAIIServerParameterControllerForTest _controller{"featureFlagInternalTransactions", true};
};
+TEST_F(LogicalSessionCacheTest, ParentAndChildSessionsHaveEqualLogicalSessionRecord) {
+ auto parentLsid = makeLogicalSessionIdForTest();
+ auto lastUse = Date_t::now();
+ auto parentSessionRecord = makeLogicalSessionRecord(parentLsid, lastUse);
+
+ auto childSessionRecord0 =
+ makeLogicalSessionRecord(makeLogicalSessionIdWithTxnNumberForTest(parentLsid), lastUse);
+ ASSERT_BSONOBJ_EQ(parentSessionRecord.toBSON(), childSessionRecord0.toBSON());
+
+ auto childSessionRecord1 =
+ makeLogicalSessionRecord(makeLogicalSessionIdWithTxnUUIDForTest(parentLsid), lastUse);
+ ASSERT_BSONOBJ_EQ(parentSessionRecord.toBSON(), childSessionRecord1.toBSON());
+}
+
// Test that promoting from the cache updates the lastUse date of records
TEST_F(LogicalSessionCacheTest, VivifyUpdatesLastUse) {
- auto lsid = makeLogicalSessionIdForTest();
+ auto runTest = [&](const LogicalSessionId& lsid) {
+ auto start = service()->now();
- auto start = service()->now();
+ // Insert the record into the sessions collection with 'start'
+ ASSERT_OK(cache()->startSession(opCtx(), makeLogicalSessionRecord(lsid, start)));
- // Insert the record into the sessions collection with 'start'
- ASSERT_OK(cache()->startSession(opCtx(), makeLogicalSessionRecord(lsid, start)));
+ // Fast forward time and promote
+ service()->fastForward(Milliseconds(500));
+ ASSERT_OK(cache()->vivify(opCtx(), lsid));
- // Fast forward time and promote
- service()->fastForward(Milliseconds(500));
- ASSERT_OK(cache()->vivify(opCtx(), lsid));
+ // Now that we promoted, lifetime of session should be extended
+ service()->fastForward(kSessionTimeout - Milliseconds(500));
+ ASSERT_OK(cache()->vivify(opCtx(), lsid));
- // Now that we promoted, lifetime of session should be extended
- service()->fastForward(kSessionTimeout - Milliseconds(500));
- ASSERT_OK(cache()->vivify(opCtx(), lsid));
+ // We promoted again, so lifetime extended again
+ service()->fastForward(kSessionTimeout - Milliseconds(500));
+ ASSERT_OK(cache()->vivify(opCtx(), lsid));
- // We promoted again, so lifetime extended again
- service()->fastForward(kSessionTimeout - Milliseconds(500));
- ASSERT_OK(cache()->vivify(opCtx(), lsid));
+ // Fast forward and promote
+ service()->fastForward(kSessionTimeout - Milliseconds(10));
+ ASSERT_OK(cache()->vivify(opCtx(), lsid));
- // Fast forward and promote
- service()->fastForward(kSessionTimeout - Milliseconds(10));
- ASSERT_OK(cache()->vivify(opCtx(), lsid));
+ // Lifetime extended again
+ service()->fastForward(Milliseconds(11));
+ ASSERT_OK(cache()->vivify(opCtx(), lsid));
+ };
- // Lifetime extended again
- service()->fastForward(Milliseconds(11));
- ASSERT_OK(cache()->vivify(opCtx(), lsid));
+ runTest(makeLogicalSessionIdForTest());
+ runTest(makeLogicalSessionIdWithTxnNumberForTest());
+ runTest(makeLogicalSessionIdWithTxnUUIDForTest());
+}
+
+TEST_F(LogicalSessionCacheTest, VivifyUpdatesLastUseOfParentSession) {
+ auto runTest = [&](const LogicalSessionId& parentLsid, const LogicalSessionId& childLsid) {
+ ASSERT_OK(
+ cache()->startSession(opCtx(), makeLogicalSessionRecord(parentLsid, service()->now())));
+ service()->fastForward(Minutes(1));
+ ASSERT_OK(cache()->vivify(opCtx(), childLsid));
+ ASSERT_OK(cache()->refreshNow(opCtx()));
+
+ auto records = sessions()->sessions();
+ ASSERT_EQ(1, records.size());
+ ASSERT_EQ(service()->now(), records.begin()->second.getLastUse());
+ sessions()->clearSessions();
+ };
+
+ auto parentLsid = makeLogicalSessionIdForTest();
+ runTest(parentLsid, makeLogicalSessionIdWithTxnNumberForTest(parentLsid));
+ runTest(parentLsid, makeLogicalSessionIdWithTxnUUIDForTest(parentLsid));
+ runTest(makeLogicalSessionIdWithTxnNumberForTest(parentLsid),
+ makeLogicalSessionIdWithTxnUUIDForTest(parentLsid));
+}
+
+TEST_F(LogicalSessionCacheTest, CannotVivifySessionWithParentSessionIfFeatureFlagIsNotEnabled) {
+ RAIIServerParameterControllerForTest controller{"featureFlagInternalTransactions", false};
+ ASSERT_THROWS_CODE(cache()->vivify(opCtx(), makeLogicalSessionIdWithTxnNumberForTest()),
+ DBException,
+ ErrorCodes::InvalidOptions);
+ ASSERT_THROWS_CODE(cache()->vivify(opCtx(), makeLogicalSessionIdWithTxnUUIDForTest()),
+ DBException,
+ ErrorCodes::InvalidOptions);
+ ASSERT_EQ(0UL, cache()->size());
}
// Test the startSession method
TEST_F(LogicalSessionCacheTest, StartSession) {
- auto record = makeLogicalSessionRecord(makeLogicalSessionIdForTest(), service()->now());
- auto lsid = record.getId();
+ auto runTest = [&](const LogicalSessionId& lsid0, const LogicalSessionId& lsid1) {
+ auto parentLsid0 = getParentSessionId(lsid0);
+ auto record = makeLogicalSessionRecord(lsid0, service()->now());
- // Test starting a new session
- ASSERT_OK(cache()->startSession(opCtx(), record));
+ // Test starting a new session
+ ASSERT_OK(cache()->startSession(opCtx(), record));
- // Record will not be in the collection yet; refresh must happen first.
- ASSERT(!sessions()->has(lsid));
+ // Record will not be in the collection yet; refresh must happen first.
+ ASSERT(!sessions()->has(lsid0));
- // Do refresh, cached records should get flushed to collection.
- ASSERT(cache()->refreshNow(opCtx()).isOK());
- ASSERT(sessions()->has(lsid));
+ // Do refresh, cached records should get flushed to collection.
+ ASSERT(cache()->refreshNow(opCtx()).isOK());
+ if (parentLsid0) {
+ ASSERT(!sessions()->has(lsid0));
+ ASSERT(sessions()->has(*parentLsid0));
+ } else {
+ ASSERT(sessions()->has(lsid0));
+ }
- // Try to start the same session again, should succeed.
- ASSERT_OK(cache()->startSession(opCtx(), record));
+ // Try to start the same session again, should succeed.
+ ASSERT_OK(cache()->startSession(opCtx(), record));
- // Try to start a session that is already in the sessions collection but
- // is not in our local cache, should succeed.
- auto record2 = makeLogicalSessionRecord(makeLogicalSessionIdForTest(), service()->now());
- sessions()->add(record2);
- ASSERT_OK(cache()->startSession(opCtx(), record2));
+ // Try to start a session that is already in the sessions collection but
+ // is not in our local cache, should succeed.
+ auto record1 = makeLogicalSessionRecord(lsid1, service()->now());
+ sessions()->add(record1);
+ ASSERT_OK(cache()->startSession(opCtx(), record1));
+
+ // Try to start a session that has expired from our cache, and is no
+ // longer in the sessions collection, should succeed
+ service()->fastForward(Milliseconds(kSessionTimeout.count() + 5));
+ sessions()->remove(parentLsid0 ? *parentLsid0 : lsid0);
+ if (parentLsid0) {
+ ASSERT(!sessions()->has(lsid0));
+ ASSERT(!sessions()->has(*parentLsid0));
+ } else {
+ ASSERT(!sessions()->has(lsid0));
+ }
+ ASSERT_OK(cache()->startSession(opCtx(), record));
+ };
- // Try to start a session that has expired from our cache, and is no
- // longer in the sessions collection, should succeed
- service()->fastForward(Milliseconds(kSessionTimeout.count() + 5));
- sessions()->remove(lsid);
- ASSERT(!sessions()->has(lsid));
- ASSERT_OK(cache()->startSession(opCtx(), record));
+ runTest(makeLogicalSessionIdForTest(), makeLogicalSessionIdForTest());
+ runTest(makeLogicalSessionIdWithTxnNumberForTest(), makeLogicalSessionIdWithTxnNumberForTest());
+ runTest(makeLogicalSessionIdWithTxnUUIDForTest(), makeLogicalSessionIdWithTxnUUIDForTest());
+}
+
+// Test the endSessions method.
+TEST_F(LogicalSessionCacheTest, EndSessions) {
+ const auto lsids = []() -> std::vector<LogicalSessionId> {
+ auto lsid0 = makeLogicalSessionIdForTest();
+ auto lsid1 = makeLogicalSessionIdForTest();
+ auto lsid2 = makeLogicalSessionIdWithTxnNumberForTest(lsid1);
+ auto lsid3 = makeLogicalSessionIdWithTxnUUIDForTest(lsid1);
+ return {lsid0, lsid1, lsid2, lsid3};
+ }();
+
+ for (const auto& lsid : lsids) {
+ ASSERT_OK(cache()->startSession(opCtx(), makeLogicalSessionRecord(lsid, service()->now())));
+ }
+ ASSERT_EQ(2UL, cache()->size());
+
+ // Verify that it is invalid to pass an lsid with a parent lsid into endSessions.
+ ASSERT_THROWS_CODE(cache()->endSessions({lsids[2]}), DBException, ErrorCodes::InvalidOptions);
+ ASSERT_THROWS_CODE(cache()->endSessions({lsids[3]}), DBException, ErrorCodes::InvalidOptions);
+
+ cache()->endSessions({lsids[0], lsids[1]});
+}
+
+// Test the peekCached method.
+TEST_F(LogicalSessionCacheTest, PeekCached) {
+ auto lsid0 = makeLogicalSessionIdForTest();
+ auto record0 = makeLogicalSessionRecord(lsid0, service()->now());
+ ASSERT_OK(cache()->startSession(opCtx(), record0));
+ ASSERT_BSONOBJ_EQ(record0.toBSON(), cache()->peekCached(lsid0)->toBSON());
+
+ // Verify that it is invalid to pass an lsid with a parent lsid into peekCached.
+ auto lsid1 = makeLogicalSessionIdWithTxnNumberForTest(lsid0);
+ ASSERT_THROWS_CODE(cache()->peekCached(lsid1), DBException, ErrorCodes::InvalidOptions);
+ auto lsid2 = makeLogicalSessionIdWithTxnUUIDForTest(lsid0);
+ ASSERT_THROWS_CODE(cache()->peekCached(lsid2), DBException, ErrorCodes::InvalidOptions);
}
// Test that session cache properly expires lsids after 30 minutes of no use
diff --git a/src/mongo/db/logical_session_id_helpers.cpp b/src/mongo/db/logical_session_id_helpers.cpp
index 6ec83fe10bc..d5565980859 100644
--- a/src/mongo/db/logical_session_id_helpers.cpp
+++ b/src/mongo/db/logical_session_id_helpers.cpp
@@ -85,6 +85,18 @@ boost::optional<LogicalSessionId> getParentSessionId(const LogicalSessionId& ses
LogicalSessionId makeLogicalSessionId(const LogicalSessionFromClient& fromClient,
OperationContext* opCtx,
std::initializer_list<Privilege> allowSpoof) {
+ uassert(ErrorCodes::InvalidOptions,
+ "Cannot specify both txnNumber and txnUUID in lsid",
+ !fromClient.getTxnNumber() || !fromClient.getTxnUUID());
+
+ uassert(ErrorCodes::InvalidOptions,
+ "Cannot specify txnNumber in lsid without specifying stmtId",
+ !fromClient.getTxnNumber() || fromClient.getStmtId());
+
+ uassert(ErrorCodes::InvalidOptions,
+ "Cannot specify stmtId in lsid without specifying txnNumber",
+ !fromClient.getStmtId() || fromClient.getTxnNumber());
+
LogicalSessionId lsid;
lsid.setId(fromClient.getId());
@@ -157,9 +169,13 @@ LogicalSessionRecord makeLogicalSessionRecord(OperationContext* opCtx, Date_t la
}
LogicalSessionRecord makeLogicalSessionRecord(const LogicalSessionId& lsid, Date_t lastUse) {
+ LogicalSessionId id{};
LogicalSessionRecord lsr{};
- lsr.setId(lsid);
+ id.setId(lsid.getId());
+ id.setUid(lsid.getUid());
+
+ lsr.setId(id);
lsr.setLastUse(lastUse);
return lsr;
diff --git a/src/mongo/db/logical_session_id_test.cpp b/src/mongo/db/logical_session_id_test.cpp
index e9043f2da01..361406952d1 100644
--- a/src/mongo/db/logical_session_id_test.cpp
+++ b/src/mongo/db/logical_session_id_test.cpp
@@ -143,6 +143,99 @@ TEST_F(LogicalSessionIdTest, ConstructorFromClientWithoutPassedUid) {
ASSERT_EQ(lsid.getUid(), user->getDigest());
}
+TEST_F(LogicalSessionIdTest, ConstructorFromClientWithTxnNumberAndStmtId) {
+ auto id = UUID::gen();
+ TxnNumber txnNumber(35);
+ StmtId stmtId(0);
+ User* user = addSimpleUser(UserName("simple", "test"));
+
+ LogicalSessionFromClient req;
+ req.setId(id);
+ req.getInternalSessionFields().setTxnNumber(txnNumber);
+ req.getInternalSessionFields().setStmtId(stmtId);
+
+ LogicalSessionId lsid = makeLogicalSessionId(req, _opCtx.get());
+ ASSERT_EQ(lsid.getId(), id);
+ ASSERT_EQ(lsid.getUid(), user->getDigest());
+ ASSERT_EQ(*lsid.getTxnNumber(), txnNumber);
+ ASSERT_EQ(*lsid.getStmtId(), stmtId);
+}
+
+TEST_F(LogicalSessionIdTest, ConstructorFromClientWithTxnUUID) {
+ auto id = UUID::gen();
+ auto txnUUID = UUID::gen();
+ User* user = addSimpleUser(UserName("simple", "test"));
+
+ LogicalSessionFromClient req;
+ req.setId(id);
+ req.getInternalSessionFields().setTxnUUID(txnUUID);
+
+ LogicalSessionId lsid = makeLogicalSessionId(req, _opCtx.get());
+ ASSERT_EQ(lsid.getId(), id);
+ ASSERT_EQ(lsid.getUid(), user->getDigest());
+ ASSERT_EQ(*lsid.getTxnUUID(), txnUUID);
+}
+
+TEST_F(LogicalSessionIdTest, ConstructorFromClientWithTxnNumberWithoutStmtId) {
+ auto id = UUID::gen();
+ TxnNumber txnNumber(35);
+ addSimpleUser(UserName("simple", "test"));
+
+ LogicalSessionFromClient req;
+ req.setId(id);
+ req.getInternalSessionFields().setTxnNumber(txnNumber);
+
+ ASSERT_THROWS_CODE(
+ makeLogicalSessionId(req, _opCtx.get()), DBException, ErrorCodes::InvalidOptions);
+}
+
+TEST_F(LogicalSessionIdTest, ConstructorFromClientWithTxnUUIDWithStmtId) {
+ auto id = UUID::gen();
+ auto txnUUID = UUID::gen();
+ StmtId stmtId(0);
+ addSimpleUser(UserName("simple", "test"));
+
+ LogicalSessionFromClient req;
+ req.setId(id);
+ req.getInternalSessionFields().setTxnUUID(txnUUID);
+ req.getInternalSessionFields().setStmtId(stmtId);
+
+ ASSERT_THROWS_CODE(
+ makeLogicalSessionId(req, _opCtx.get()), DBException, ErrorCodes::InvalidOptions);
+}
+
+TEST_F(LogicalSessionIdTest, ConstructorFromClientWithTxnNumberAndTxnUUID) {
+ auto id = UUID::gen();
+ TxnNumber txnNumber(35);
+ auto txnUUID = UUID::gen();
+ addSimpleUser(UserName("simple", "test"));
+
+ LogicalSessionFromClient req;
+ req.setId(id);
+ req.getInternalSessionFields().setTxnNumber(txnNumber);
+ req.getInternalSessionFields().setTxnUUID(txnUUID);
+
+ ASSERT_THROWS_CODE(
+ makeLogicalSessionId(req, _opCtx.get()), DBException, ErrorCodes::InvalidOptions);
+}
+
+TEST_F(LogicalSessionIdTest, ConstructorFromClientWithTxnNumberAndTxnUUIDAndStmtId) {
+ auto id = UUID::gen();
+ TxnNumber txnNumber(35);
+ auto txnUUID = UUID::gen();
+ StmtId stmtId(0);
+ addSimpleUser(UserName("simple", "test"));
+
+ LogicalSessionFromClient req;
+ req.setId(id);
+ req.getInternalSessionFields().setTxnNumber(txnNumber);
+ req.getInternalSessionFields().setTxnUUID(txnUUID);
+ req.getInternalSessionFields().setStmtId(stmtId);
+
+ ASSERT_THROWS_CODE(
+ makeLogicalSessionId(req, _opCtx.get()), DBException, ErrorCodes::InvalidOptions);
+}
+
TEST_F(LogicalSessionIdTest, ConstructorFromClientWithoutPassedUidAndWithoutAuthedUser) {
auto id = UUID::gen();
diff --git a/src/mongo/db/session_catalog.cpp b/src/mongo/db/session_catalog.cpp
index 581d65aa27c..7a87b2f6f85 100644
--- a/src/mongo/db/session_catalog.cpp
+++ b/src/mongo/db/session_catalog.cpp
@@ -35,6 +35,9 @@
#include <memory>
+#include "mongo/db/internal_transactions_feature_flag_gen.h"
+#include "mongo/db/logical_session_id_helpers.h"
+#include "mongo/db/server_options.h"
#include "mongo/db/service_context.h"
#include "mongo/logv2/log.h"
@@ -73,6 +76,11 @@ SessionCatalog* SessionCatalog::get(ServiceContext* service) {
SessionCatalog::ScopedCheckedOutSession SessionCatalog::_checkOutSessionWithParentSession(
OperationContext* opCtx, const LogicalSessionId& lsid, boost::optional<KillToken> killToken) {
+ uassert(ErrorCodes::InvalidOptions,
+ "Internal transactions are not enabled",
+ feature_flags::gFeatureFlagInternalTransactions.isEnabled(
+ serverGlobalParams.featureCompatibility));
+
if (killToken) {
invariant(killToken->lsidToKill == lsid);
} else {
diff --git a/src/mongo/db/session_catalog_test.cpp b/src/mongo/db/session_catalog_test.cpp
index a89ee2561c1..209e68a74e0 100644
--- a/src/mongo/db/session_catalog_test.cpp
+++ b/src/mongo/db/session_catalog_test.cpp
@@ -36,6 +36,7 @@
#include "mongo/db/cancelable_operation_context.h"
#include "mongo/db/service_context_test_fixture.h"
#include "mongo/db/session_catalog.h"
+#include "mongo/idl/server_parameter_test_util.h"
#include "mongo/logv2/log.h"
#include "mongo/stdx/future.h"
#include "mongo/unittest/barrier.h"
@@ -52,6 +53,8 @@ protected:
SessionCatalog* catalog() {
return SessionCatalog::get(getServiceContext());
}
+
+ RAIIServerParameterControllerForTest _controller{"featureFlagInternalTransactions", true};
};
class SessionCatalogTestWithDefaultOpCtx : public SessionCatalogTest {
@@ -77,6 +80,14 @@ private:
const bool _wasInDirectClient;
};
+TEST_F(SessionCatalogTest, GetParentSessionId) {
+ auto parentLsid = makeLogicalSessionIdForTest();
+ ASSERT(!getParentSessionId(parentLsid).has_value());
+ ASSERT_EQ(parentLsid,
+ *getParentSessionId(makeLogicalSessionIdWithTxnNumberForTest(parentLsid)));
+ ASSERT_EQ(parentLsid, *getParentSessionId(makeLogicalSessionIdWithTxnUUIDForTest(parentLsid)));
+}
+
TEST_F(SessionCatalogTestWithDefaultOpCtx, CheckoutAndReleaseSession) {
_opCtx->setLogicalSessionId(makeLogicalSessionIdForTest());
OperationContextSession ocs(_opCtx);
@@ -108,6 +119,19 @@ TEST_F(SessionCatalogTestWithDefaultOpCtx, CheckoutAndReleaseSessionWithTxnUUID)
ASSERT_EQ(*_opCtx->getLogicalSessionId(), session->getSessionId());
}
+TEST_F(SessionCatalogTestWithDefaultOpCtx,
+ CannotCheckoutSessionWithParentSessionIfFeatureFlagIsNotEnabled) {
+ RAIIServerParameterControllerForTest controller{"featureFlagInternalTransactions", false};
+
+ _opCtx->setLogicalSessionId(makeLogicalSessionIdWithTxnNumberForTest());
+ ASSERT_THROWS_CODE(OperationContextSession(_opCtx), DBException, ErrorCodes::InvalidOptions);
+
+ _opCtx->setLogicalSessionId(makeLogicalSessionIdWithTxnUUIDForTest());
+ ASSERT_THROWS_CODE(OperationContextSession(_opCtx), DBException, ErrorCodes::InvalidOptions);
+
+ ASSERT_EQ(0UL, catalog()->size());
+}
+
TEST_F(SessionCatalogTestWithDefaultOpCtx, CannotCheckOutParentSessionOfCheckedOutSession) {
auto runTest = [&](const LogicalSessionId& parentLsid, const LogicalSessionId& childLsid) {
_opCtx->setLogicalSessionId(childLsid);
diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript
index bd95a41d023..f1d5a83891e 100644
--- a/src/mongo/s/SConscript
+++ b/src/mongo/s/SConscript
@@ -148,7 +148,6 @@ env.Library(
'chunk_version.idl',
'database_version.cpp',
'database_version.idl',
- 'internal_transactions_feature_flag.idl',
'load_balancer_feature_flag.idl',
'mongod_and_mongos_server_parameters.idl',
'request_types/abort_reshard_collection.idl',