/** * Test that a cursor cannot be timed out while in use, and that it's time of last use is updated * when it is unpinned. */ TEST_F(CursorManagerTest, CursorShouldNotTimeOutUntilIdleForLongEnoughAfterBeingUnpinned) { CursorManager* cursorManager = useCursorManager(); auto clock = useClock(); // Register a cursor which we will look at again. auto cursorPin = cursorManager->registerCursor(_opCtx.get(), {makeFakePlanExecutor(), kTestNss, {}, repl::ReadConcernLevel::kLocalReadConcern, BSONObj()}); // Advance the clock to simulate time passing. clock->advance(getDefaultCursorTimeoutMillis() + Milliseconds(1)); // Make sure the pinned cursor does not time out, before or after unpinning it. ASSERT_EQ(1UL, cursorManager->numCursors()); ASSERT_EQ(0UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now())); ASSERT_EQ(1UL, cursorManager->numCursors()); cursorPin.release(); ASSERT_EQ(1UL, cursorManager->numCursors()); ASSERT_EQ(0UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now())); ASSERT_EQ(1UL, cursorManager->numCursors()); // Advance the clock to simulate more time passing, then assert that the now-inactive cursor // times out. clock->advance(getDefaultCursorTimeoutMillis() + Milliseconds(1)); ASSERT_EQ(1UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now())); ASSERT_EQ(0UL, cursorManager->numCursors()); }
/** * Test that client cursors time out and get deleted. */ TEST_F(CursorManagerTest, InactiveCursorShouldTimeout) { CursorManager* cursorManager = useCursorManager(); auto clock = useClock(); cursorManager->registerCursor(_opCtx.get(), {makeFakePlanExecutor(), NamespaceString{"test.collection"}, {}, repl::ReadConcernLevel::kLocalReadConcern, BSONObj()}); ASSERT_EQ(0UL, cursorManager->timeoutCursors(_opCtx.get(), Date_t())); clock->advance(getDefaultCursorTimeoutMillis()); ASSERT_EQ(1UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now())); ASSERT_EQ(0UL, cursorManager->numCursors()); cursorManager->registerCursor(_opCtx.get(), {makeFakePlanExecutor(), NamespaceString{"test.collection"}, {}, repl::ReadConcernLevel::kLocalReadConcern, BSONObj()}); ASSERT_EQ(1UL, cursorManager->timeoutCursors(_opCtx.get(), Date_t::max())); ASSERT_EQ(0UL, cursorManager->numCursors()); }
/** * Test that using a cursor updates its time of last use. */ TEST_F(CursorManagerTest, UsingACursorShouldUpdateTimeOfLastUse) { CursorManager* cursorManager = useCursorManager(); auto clock = useClock(); // Register a cursor which we will look at again. auto cursorPin = cursorManager->registerCursor( _opCtx.get(), {makeFakePlanExecutor(), kTestNss, {}, false, BSONObj()}); auto usedCursorId = cursorPin.getCursor()->cursorid(); cursorPin.release(); // Register a cursor to immediately forget about, to make sure it will time out on a normal // schedule. cursorManager->registerCursor(_opCtx.get(), {makeFakePlanExecutor(), kTestNss, {}, false, BSONObj()}); // Advance the clock to simulate time passing. clock->advance(Milliseconds(1)); // Touch the cursor with id 'usedCursorId' to advance its time of last use. cursorManager->pinCursor(_opCtx.get(), usedCursorId).status_with_transitional_ignore(); // We should be able to time out the unused cursor, but the one we used should stay alive. ASSERT_EQ(2UL, cursorManager->numCursors()); clock->advance(getDefaultCursorTimeoutMillis() - Milliseconds(1)); ASSERT_EQ(1UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now())); ASSERT_EQ(1UL, cursorManager->numCursors()); // We should be able to time out the used cursor after one more millisecond. clock->advance(Milliseconds(1)); ASSERT_EQ(1UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now())); ASSERT_EQ(0UL, cursorManager->numCursors()); }
/** * Test that pinned cursors do not get timed out. */ TEST_F(CursorManagerTest, InactivePinnedCursorShouldNotTimeout) { CursorManager* cursorManager = useCursorManager(); auto clock = useClock(); auto cursorPin = cursorManager->registerCursor( _opCtx.get(), {makeFakePlanExecutor(), NamespaceString{"test.collection"}, {}, false, BSONObj()}); // The pin is still in scope, so it should not time out. clock->advance(getDefaultCursorTimeoutMillis()); ASSERT_EQ(0UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now())); }
/** * Test that client cursors which have been marked as killed but are still pinned *do not* time out. */ TEST_F(CursorManagerTest, InactiveKilledCursorsThatAreStillPinnedShouldNotTimeout) { CursorManager* cursorManager = useCursorManager(); auto clock = useClock(); // Make a cursor from the plan executor, and immediately kill it. auto cursorPin = cursorManager->registerCursor( _opCtx.get(), {makeFakePlanExecutor(), NamespaceString{"test.collection"}, {}, false, BSONObj()}); const bool collectionGoingAway = false; cursorManager->invalidateAll( _opCtx.get(), collectionGoingAway, "KilledCursorsShouldTimeoutTest"); // Advance the clock to simulate time passing. clock->advance(getDefaultCursorTimeoutMillis()); // The pin is still in scope, so it should not time out. ASSERT_EQ(0UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now())); }
/** * Test that client cursors which have been marked as killed time out and get deleted. */ TEST_F(CursorManagerTest, InactiveKilledCursorsShouldTimeout) { CursorManager* cursorManager = useCursorManager(); auto clock = useClock(); // Make a cursor from the plan executor, and immediately kill it. auto cursorPin = cursorManager->registerCursor( _opCtx.get(), {makeFakePlanExecutor(), NamespaceString{"test.collection"}, {}, false, BSONObj()}); cursorPin.release(); const bool collectionGoingAway = false; cursorManager->invalidateAll( _opCtx.get(), collectionGoingAway, "KilledCursorsShouldTimeoutTest"); // Advance the clock to simulate time passing. clock->advance(Milliseconds(CursorManager::kDefaultCursorTimeoutMinutes)); ASSERT_EQ(1UL, cursorManager->timeoutCursors(_opCtx.get(), clock->now())); ASSERT_EQ(0UL, cursorManager->numCursors()); }