TEST_F(TransactionCoordinatorCatalogTest, CreateFollowedByGetReturnsCoordinator) { LogicalSessionId lsid = makeLogicalSessionIdForTest(); TxnNumber txnNumber = 1; createCoordinatorInCatalog(operationContext(), lsid, txnNumber); auto coordinatorInCatalog = coordinatorCatalog().get(operationContext(), lsid, txnNumber); ASSERT(coordinatorInCatalog != nullptr); }
/** * Test a manager that has one cursor running inside of a session. */ TEST_F(CursorManagerTestCustomOpCtx, OneCursorWithASession) { // Add a cursor with a session to the cursor manager. auto lsid = makeLogicalSessionIdForTest(); auto opCtx = _queryServiceContext->makeOperationContext(lsid, boost::none); auto pinned = makeCursor(opCtx.get()); // Retrieve all sessions active in manager - set should contain just lsid. LogicalSessionIdSet lsids; useCursorManager()->appendActiveSessions(&lsids); ASSERT_EQ(lsids.size(), size_t(1)); ASSERT(lsids.find(lsid) != lsids.end()); // Retrieve all cursors for this lsid - should be just ours. auto cursors = useCursorManager()->getCursorsForSession(lsid); ASSERT_EQ(cursors.size(), size_t(1)); auto cursorId = pinned.getCursor()->cursorid(); ASSERT(cursors.find(cursorId) != cursors.end()); // Remove the cursor from the manager. pinned.release(); ASSERT_OK(useCursorManager()->killCursor(opCtx.get(), cursorId, false)); // There should be no more cursor entries by session id. LogicalSessionIdSet sessions; useCursorManager()->appendActiveSessions(&sessions); ASSERT(sessions.empty()); ASSERT(useCursorManager()->getCursorsForSession(lsid).empty()); }
TEST_F(CursorManagerTestCustomOpCtx, KillCursorRespectsSessionId) { // Add a cursor with a session to the cursor manager. auto lsid = makeLogicalSessionIdForTest(); auto opCtx = _queryServiceContext->makeOperationContext(lsid, boost::none); auto pinned = makeCursor(opCtx.get()); auto cursorId = pinned.getCursor()->cursorid(); // Killing the cursor with incorrect LogicalSessionId fails. pinned.release(); auto wrongLsid = makeLogicalSessionIdForTest(); auto status = useCursorManager()->killCursor(opCtx.get(), cursorId, false, wrongLsid, boost::none); ASSERT_NOT_OK(status); ASSERT_EQ(status.code(), ErrorCodes::CursorNotFound); // Killing the cursor with the correct LogicalSessionId works. ASSERT_OK(useCursorManager()->killCursor(opCtx.get(), cursorId, false, lsid, boost::none)); }
TEST_F(TransactionCoordinatorCatalogTest, SecondCreateForSessionDoesNotOverwriteFirstCreate) { LogicalSessionId lsid = makeLogicalSessionIdForTest(); TxnNumber txnNumber1 = 1; TxnNumber txnNumber2 = 2; createCoordinatorInCatalog(operationContext(), lsid, txnNumber1); createCoordinatorInCatalog(operationContext(), lsid, txnNumber2); auto coordinator1InCatalog = coordinatorCatalog().get(operationContext(), lsid, txnNumber1); ASSERT(coordinator1InCatalog != nullptr); }
/** * Test a manager with multiple cursors running inside of different sessions. */ TEST_F(CursorManagerTestCustomOpCtx, MultipleCursorsMultipleSessions) { auto lsid1 = makeLogicalSessionIdForTest(); auto lsid2 = makeLogicalSessionIdForTest(); CursorId cursor1; CursorId cursor2; // Cursor with session 1. { auto opCtx1 = _queryServiceContext->makeOperationContext(lsid1, boost::none); cursor1 = makeCursor(opCtx1.get()).getCursor()->cursorid(); } // Cursor with session 2. { auto opCtx2 = _queryServiceContext->makeOperationContext(lsid2, boost::none); cursor2 = makeCursor(opCtx2.get()).getCursor()->cursorid(); } // Cursor with no session. { auto opCtx3 = _queryServiceContext->makeOperationContext(); makeCursor(opCtx3.get()).getCursor(); } // Retrieve all sessions - should be both lsids. LogicalSessionIdSet lsids; useCursorManager()->appendActiveSessions(&lsids); ASSERT_EQ(lsids.size(), size_t(2)); ASSERT(lsids.find(lsid1) != lsids.end()); ASSERT(lsids.find(lsid2) != lsids.end()); // Retrieve cursors for each session - should be just one. auto cursors1 = useCursorManager()->getCursorsForSession(lsid1); ASSERT_EQ(cursors1.size(), size_t(1)); ASSERT(cursors1.find(cursor1) != cursors1.end()); auto cursors2 = useCursorManager()->getCursorsForSession(lsid2); ASSERT_EQ(cursors2.size(), size_t(1)); ASSERT(cursors2.find(cursor2) != cursors2.end()); }
TEST(OperationContextTest, SessionIdAndTransactionNumber) { auto serviceCtx = ServiceContext::make(); auto client = serviceCtx->makeClient("OperationContextTest"); auto opCtx = client->makeOperationContext(); const auto lsid = makeLogicalSessionIdForTest(); opCtx->setLogicalSessionId(lsid); opCtx->setTxnNumber(5); ASSERT(opCtx->getTxnNumber()); ASSERT_EQUALS(5, *opCtx->getTxnNumber()); }
TEST_F(CursorManagerTestCustomOpCtx, KillCursorWithSessionDoesNotKillCursorCreatedOutsideOfSession) { // Add a cursor with a session to the cursor manager. auto opCtx = _queryServiceContext->makeOperationContext(); auto pinned = makeCursor(opCtx.get()); auto cursorId = pinned.getCursor()->cursorid(); // Killing the cursor with the correct cursorId but with an unrelated LogicalSessionId fails. auto lsid = makeLogicalSessionIdForTest(); auto status = useCursorManager()->killCursor(opCtx.get(), cursorId, false, lsid, boost::none); ASSERT_NOT_OK(status); ASSERT_EQ(status.code(), ErrorCodes::CursorNotFound); }
BSONObj ClusterCommandTestFixture::_makeCmd(BSONObj cmdObj, bool includeAfterClusterTime) { BSONObjBuilder bob(cmdObj); // Each command runs in a new session. bob.append("lsid", makeLogicalSessionIdForTest().toBSON()); bob.append("txnNumber", TxnNumber(1)); bob.append("autocommit", false); bob.append("startTransaction", true); BSONObjBuilder readConcernBob = bob.subobjStart(repl::ReadConcernArgs::kReadConcernFieldName); readConcernBob.append("level", "snapshot"); if (includeAfterClusterTime) { readConcernBob.append("afterClusterTime", kAfterClusterTime); } readConcernBob.doneFast(); return bob.obj(); }
/** * Test that cursors inherit the logical session id from their operation context */ TEST_F(CursorManagerTestCustomOpCtx, LogicalSessionIdOnOperationCtxTest) { // Cursors created on an op ctx without a session id have no session id. { auto opCtx = _queryServiceContext->makeOperationContext(); auto pinned = makeCursor(opCtx.get()); ASSERT_EQUALS(pinned.getCursor()->getSessionId(), boost::none); } // Cursors created on an op ctx with a session id have a session id. { auto lsid = makeLogicalSessionIdForTest(); auto opCtx2 = _queryServiceContext->makeOperationContext(lsid, boost::none); auto pinned2 = makeCursor(opCtx2.get()); ASSERT_EQUALS(pinned2.getCursor()->getSessionId(), lsid); } }
/** * Test a manager with multiple cursors running inside of the same session. */ TEST_F(CursorManagerTestCustomOpCtx, MultipleCursorsWithSameSession) { // Add two cursors on the same session to the cursor manager. auto lsid = makeLogicalSessionIdForTest(); auto opCtx = _queryServiceContext->makeOperationContext(lsid, boost::none); auto pinned = makeCursor(opCtx.get()); auto pinned2 = makeCursor(opCtx.get()); auto cursorId1 = pinned.getCursor()->cursorid(); auto cursorId2 = pinned2.getCursor()->cursorid(); // Retrieve all sessions - set should contain just lsid. stdx::unordered_set<LogicalSessionId, LogicalSessionIdHash> lsids; useCursorManager()->appendActiveSessions(&lsids); ASSERT_EQ(lsids.size(), size_t(1)); ASSERT(lsids.find(lsid) != lsids.end()); // Retrieve all cursors for session - should be both cursors. auto cursors = useCursorManager()->getCursorsForSession(lsid); ASSERT_EQ(cursors.size(), size_t(2)); ASSERT(cursors.find(cursorId1) != cursors.end()); ASSERT(cursors.find(cursorId2) != cursors.end()); // Remove one cursor from the manager. pinned.release(); ASSERT_OK(useCursorManager()->killCursor(opCtx.get(), cursorId1, false)); // Should still be able to retrieve the session. lsids.clear(); useCursorManager()->appendActiveSessions(&lsids); ASSERT_EQ(lsids.size(), size_t(1)); ASSERT(lsids.find(lsid) != lsids.end()); // Should still be able to retrieve remaining cursor by session. cursors = useCursorManager()->getCursorsForSession(lsid); ASSERT_EQ(cursors.size(), size_t(1)); ASSERT(cursors.find(cursorId2) != cursors.end()); }
TEST_F(CursorManagerTestCustomOpCtx, KillAllCursorsForTransactionRemovesCorrectEntryFromTransactionMap) { CursorManager* cursorManager = CursorManager::getGlobalCursorManager(); // Create 3 sets of cursors, each with a unique LogicalSessionId/TxnNumber pair, but each // sharing either LogicalSessionId or TxnNumber with another set. auto lsid1 = makeLogicalSessionIdForTest(); TxnNumber txnNumber1 = 0; { auto opCtx = _queryServiceContext->makeOperationContext(lsid1, txnNumber1); auto pinned = cursorManager->registerCursor(opCtx.get(), {makeFakePlanExecutor(), NamespaceString{"test.collection"}, {}, repl::ReadConcernLevel::kLocalReadConcern, BSONObj()}); pinned.release(); } auto lsid2 = lsid1; TxnNumber txnNumber2 = 1; { auto opCtx = _queryServiceContext->makeOperationContext(lsid2, txnNumber2); auto pinned = cursorManager->registerCursor(opCtx.get(), {makeFakePlanExecutor(), NamespaceString{"test.collection"}, {}, repl::ReadConcernLevel::kLocalReadConcern, BSONObj()}); pinned.release(); } auto lsid3 = makeLogicalSessionIdForTest(); TxnNumber txnNumber3 = txnNumber1; { auto opCtx = _queryServiceContext->makeOperationContext(lsid3, txnNumber3); // Create 2 cursors for the third set to confirm multiple cursor deregistration. auto pinned = cursorManager->registerCursor(opCtx.get(), {makeFakePlanExecutor(), NamespaceString{"test.collection"}, {}, repl::ReadConcernLevel::kLocalReadConcern, BSONObj()}); pinned.release(); pinned = cursorManager->registerCursor(opCtx.get(), {makeFakePlanExecutor(), NamespaceString{"test.collection"}, {}, repl::ReadConcernLevel::kLocalReadConcern, BSONObj()}); pinned.release(); } auto opCtx = _queryServiceContext->makeOperationContext(); // Transaction reference exists for all 3 sets. ASSERT_TRUE(cursorManager->hasTransactionCursorReference(lsid1, txnNumber1)); ASSERT_TRUE(cursorManager->hasTransactionCursorReference(lsid2, txnNumber2)); ASSERT_TRUE(cursorManager->hasTransactionCursorReference(lsid3, txnNumber3)); // Transaction reference does not exist for LogicalSessionId/TxnNumber that has no cursors. ASSERT_FALSE(cursorManager->hasTransactionCursorReference(makeLogicalSessionIdForTest(), 99)); // Kill cursors for set 1. ASSERT_EQ(1ul, cursorManager->killAllCursorsForTransaction(opCtx.get(), lsid1, txnNumber1)); ASSERT_FALSE(cursorManager->hasTransactionCursorReference(lsid1, txnNumber1)); ASSERT_TRUE(cursorManager->hasTransactionCursorReference(lsid2, txnNumber2)); ASSERT_TRUE(cursorManager->hasTransactionCursorReference(lsid3, txnNumber3)); // Kill cursors for set 2. ASSERT_EQ(1ul, cursorManager->killAllCursorsForTransaction(opCtx.get(), lsid2, txnNumber2)); ASSERT_FALSE(cursorManager->hasTransactionCursorReference(lsid2, txnNumber2)); ASSERT_TRUE(cursorManager->hasTransactionCursorReference(lsid3, txnNumber3)); // Kill cursors for set 3. ASSERT_EQ(2ul, cursorManager->killAllCursorsForTransaction(opCtx.get(), lsid3, txnNumber3)); ASSERT_FALSE(cursorManager->hasTransactionCursorReference(lsid3, txnNumber3)); }
TEST_F(TransactionCoordinatorCatalogTest, SecondCreateForSessionDoesNotOverwriteFirstCreate) { LogicalSessionId lsid = makeLogicalSessionIdForTest(); TxnNumber txnNumber1 = 1; TxnNumber txnNumber2 = 2; createCoordinatorInCatalog(operationContext(), lsid, txnNumber1); createCoordinatorInCatalog(operationContext(), lsid, txnNumber2); auto coordinator1InCatalog = coordinatorCatalog().get(operationContext(), lsid, txnNumber1); ASSERT(coordinator1InCatalog != nullptr); } DEATH_TEST_F(TransactionCoordinatorCatalogTest, CreatingACoordinatorWithASessionIdAndTxnNumberThatAlreadyExistFails, "Invariant failure") { LogicalSessionId lsid = makeLogicalSessionIdForTest(); TxnNumber txnNumber = 1; createCoordinatorInCatalog(operationContext(), lsid, txnNumber); // Re-creating w/ same session id and txn number should cause invariant failure createCoordinatorInCatalog(operationContext(), lsid, txnNumber); } TEST_F(TransactionCoordinatorCatalogTest, GetLatestOnSessionWithNoCoordinatorsReturnsNone) { LogicalSessionId lsid = makeLogicalSessionIdForTest(); auto latestTxnNumAndCoordinator = coordinatorCatalog().getLatestOnSession(operationContext(), lsid); ASSERT_FALSE(latestTxnNumAndCoordinator); } TEST_F(TransactionCoordinatorCatalogTest, CreateFollowedByGetLatestOnSessionReturnsOnlyCoordinator) {