public void Cursor_should_unpin_connection_for_operations_under_the_same_transaction_after_abortTransaction_and_cursor_dispose( [Values(1, 3)] int attempts, [Values(false, true)] bool forceCursorClose, [Values(false, true)] bool async) { SkipIfNotLoadBalancingMode(); KillOpenTransactions(); SetupData(); var eventCapturer = new EventCapturer() .Capture <ConnectionPoolCheckedOutConnectionEvent>() .Capture <ConnectionPoolCheckingOutConnectionEvent>() .Capture <ConnectionPoolCheckedInConnectionEvent>() .Capture <ConnectionPoolCheckingInConnectionEvent>() .Capture <CommandSucceededEvent>(); List <IAsyncCursor <BsonDocument> > cursors = new(); using (var cluster = CreateLoadBalancedCluster(eventCapturer)) { eventCapturer.Clear(); ICoreSessionHandle session; DisposableBindingBundle <IReadBindingHandle, RetryableReadContext> readBindingsBundle = null; using (session = CreateSession(cluster, isImplicit: false, withTransaction: true)) { for (int i = 1; i <= attempts; i++) { AssertSessionReferenceCount(session, i); // dynamic value because we don't close cursors in the loop IAsyncCursor <BsonDocument> asyncCursor; eventCapturer.Any().Should().BeFalse(); using (readBindingsBundle = CreateReadBindingsAndRetryableReadContext(cluster, session.Fork(), async)) { AssertCheckOutOnlyEvents(eventCapturer, i, shouldNextAttemptTriggerCheckout: false); asyncCursor = CreateAndRunFindOperation(readBindingsBundle.RetryableContext, async); AssertCommand(eventCapturer, "find", noMoreEvents: true); } MoveNext(asyncCursor, async).Should().BeTrue(); // no op MoveNext(asyncCursor, async).Should().BeTrue(); AssertCommand(eventCapturer, "getMore", noMoreEvents: true); cursors.Add(asyncCursor); } AbortTransaction(session, async); AssertCommand(eventCapturer, "abortTransaction", noMoreEvents: true); } for (int i = 0; i < cursors.Count; i++) { IAsyncCursor <BsonDocument> cursor = cursors[i]; if (forceCursorClose) { cursor.Dispose(); } else { var exception = Record.Exception(() => cursor.MoveNext()); exception .Should() .BeOfType <MongoCommandException>() .Subject .Message .Should() .StartWith("Command getMore failed: Cannot run getMore on cursor") .And .EndWith("without a txnNumber."); cursor.Dispose(); } AssertCommand(eventCapturer, "killCursors", noMoreEvents: i < cursors.Count - 1); } AssertCheckInOnlyEvents(eventCapturer); AssertSessionReferenceCount(session, 0); AssertChannelReferenceCount(readBindingsBundle.RetryableContext.Channel, 0); } }
public void Cursor_should_pin_connection_in_transaction_with_the_same_session_as_expected( [Values(1, 4)] int attempts, [Values(false, true)] bool forceCursorClose, [Values(false)] bool async) { SkipIfNotLoadBalancingMode(); KillOpenTransactions(); SetupData(); var eventCapturer = new EventCapturer() .Capture <ConnectionPoolCheckedOutConnectionEvent>() .Capture <ConnectionPoolCheckingOutConnectionEvent>() .Capture <ConnectionPoolCheckedInConnectionEvent>() .Capture <ConnectionPoolCheckingInConnectionEvent>() .Capture <CommandSucceededEvent>(); List <IAsyncCursor <BsonDocument> > cursors = new(); using (var cluster = CreateLoadBalancedCluster(eventCapturer)) { eventCapturer.Clear(); ICoreSessionHandle session; DisposableBindingBundle <IReadBindingHandle, RetryableReadContext> readBindingsBundle = null; using (session = CreateSession(cluster, isImplicit: false, withTransaction: true)) { for (int i = 1; i <= attempts; i++) { AssertSessionReferenceCount(session, i); // dynamic value because we don't close cursors in the loop IAsyncCursor <BsonDocument> asyncCursor; eventCapturer.Any().Should().BeFalse(); using (readBindingsBundle = CreateReadBindingsAndRetryableReadContext(cluster, session.Fork(), async)) { AssertCheckOutOnlyEvents(eventCapturer, i, shouldNextAttemptTriggerCheckout: false); asyncCursor = CreateAndRunFindOperation(readBindingsBundle.RetryableContext, async); AssertCommand(eventCapturer, "find", noMoreEvents: true); } MoveNext(asyncCursor, async).Should().BeTrue(); // no op MoveNext(asyncCursor, async).Should().BeTrue(); AssertCommand(eventCapturer, "getMore", noMoreEvents: true); cursors.Add(asyncCursor); } } for (int i = 0; i < cursors.Count; i++) { IAsyncCursor <BsonDocument> cursor = cursors[i]; if (forceCursorClose) { cursor.Dispose(); AssertCommand(eventCapturer, "killCursors", noMoreEvents: i < cursors.Count - 1); } else { MoveNext(cursor, async).Should().BeTrue(); // returns cursorId = 0 MoveNext(cursor, async).Should().BeFalse(); AssertCommand(eventCapturer, "getMore", noMoreEvents: i < cursors.Count - 1); } } AssertCommand(eventCapturer, "abortTransaction", noMoreEvents: false); AssertCheckInOnlyEvents(eventCapturer); AssertSessionReferenceCount(session, 0); AssertChannelReferenceCount(readBindingsBundle.RetryableContext.Channel, 0); } }