public void Dispose_should_dispose_the_server() { _subject.Initialize(); _capturedEvents.Clear(); _subject.Dispose(); _mockConnectionPool.Verify(p => p.Dispose(), Times.Once); _capturedEvents.Next().Should().BeOfType <ServerClosingEvent>(); _capturedEvents.Next().Should().BeOfType <ServerClosedEvent>(); _capturedEvents.Any().Should().BeFalse(); }
public void Dispose_should_dispose_the_server() { _subject.Initialize(); _capturedEvents.Clear(); _subject.Dispose(); _connectionPool.Received().Dispose(); _serverMonitor.Received().Dispose(); _capturedEvents.Next().Should().BeOfType <ServerClosingEvent>(); _capturedEvents.Next().Should().BeOfType <ServerClosedEvent>(); _capturedEvents.Any().Should().BeFalse(); }
public void Description_should_not_contain_any_servers_if_the_provided_server_is_not_of_the_required_type(ClusterConnectionMode connectionMode, ServerType serverType) { _settings = _settings.With(connectionMode: connectionMode); var subject = CreateSubject(); subject.Initialize(); _capturedEvents.Clear(); PublishDescription(_endPoint, serverType); subject.Description.Servers.Should().BeEmpty(); _capturedEvents.Next().Should().BeOfType <ClusterDescriptionChangedEvent>(); _capturedEvents.Any().Should().BeFalse(); }
public void Connection_pool_should_be_cleared_when_Shutdown_exceptions( [Values( ServerErrorCode.ShutdownInProgress, // 91 ServerErrorCode.InterruptedAtShutdown)] // 11600 int errorCode) { RequireServer.Check().Supports(Feature.FailPointsFailCommand).ClusterType(ClusterType.ReplicaSet); var eventCapturer = new EventCapturer().Capture <ConnectionPoolRemovedConnectionEvent>(); using (var client = CreateDisposableClient(eventCapturer)) { var database = client.GetDatabase(_databaseName, new MongoDatabaseSettings { WriteConcern = WriteConcern.WMajority }); database.DropCollection(_databaseName); var collection = database.GetCollection <BsonDocument>(_collectionName, new MongoCollectionSettings { WriteConcern = WriteConcern.WMajority }); eventCapturer.Clear(); using (ConfigureFailPoint(client, errorCode)) { var exception = Record.Exception(() => { collection.InsertOne(new BsonDocument("test", 1)); }); var e = exception.Should().BeOfType <MongoNodeIsRecoveringException>().Subject; e.Code.Should().Be(errorCode); } eventCapturer.Events.Count.Should().Be(1); } }
public void Connection_pool_should_work_as_expected_when_NonMaster_exception() { RequireServer.Check().Supports(Feature.FailPointsFailCommand).ClusterType(ClusterType.ReplicaSet); var shouldConnectionPoolBeCleared = !Feature.KeepConnectionPoolWhenNotMasterConnectionException.IsSupported(CoreTestConfiguration.ServerVersion); var eventCapturer = new EventCapturer().Capture <ConnectionPoolRemovedConnectionEvent>(); using (var client = CreateDisposableClient(eventCapturer)) { var database = client.GetDatabase(_databaseName, new MongoDatabaseSettings { WriteConcern = WriteConcern.WMajority }); database.DropCollection(_databaseName); var collection = database.GetCollection <BsonDocument>(_collectionName, new MongoCollectionSettings { WriteConcern = WriteConcern.WMajority }); eventCapturer.Clear(); using (ConfigureFailPoint(client, 10107)) { var document = new BsonDocument("test", 1); var exception = Record.Exception(() => { collection.InsertOne(document); }); var e = exception.Should().BeOfType <MongoNotPrimaryException>().Subject; e.Code.Should().Be(10107); if (!shouldConnectionPoolBeCleared) { collection.InsertOne(document); } } eventCapturer.Events.Count.Should().Be(shouldConnectionPoolBeCleared ? 1 : 0); } }
public void Should_call_custom_server_selector() { var eventCapturer = new EventCapturer() .Capture <ClusterSelectingServerEvent>() .Capture <ClusterSelectedServerEvent>(); var customServerSelector = new CustomServerSelector(); using (var client = DriverTestConfiguration.CreateDisposableClient( clientSettings => clientSettings.ClusterConfigurator = c => { c.ConfigureCluster(s => s.With(postServerSelector: customServerSelector)); c.Subscribe(eventCapturer); }, logger: null)) { var collection = client .GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection <BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName) .WithReadPreference(ReadPreference.Nearest); customServerSelector.CustomSelectorWasCalled = false; eventCapturer.Clear(); collection.CountDocuments(new BsonDocument()); customServerSelector.CustomSelectorWasCalled.Should().BeTrue(); eventCapturer.Next().Should().BeOfType <ClusterSelectingServerEvent>(); eventCapturer.Next().Should().BeOfType <ClusterSelectedServerEvent>(); } }
public void Setup() { _capturedEvents = new EventCapturer() .Capture<CommandStartedEvent>() .Capture<CommandSucceededEvent>() .Capture<CommandFailedEvent>(); _streamFactory = Substitute.For<IStreamFactory>(); _endPoint = new DnsEndPoint("localhost", 27017); var serverId = new ServerId(new ClusterId(), _endPoint); _connectionInitializer = Substitute.For<IConnectionInitializer>(); _connectionInitializer.InitializeConnectionAsync(null, CancellationToken.None) .ReturnsForAnyArgs(Task.FromResult(new ConnectionDescription( new ConnectionId(serverId), new IsMasterResult(new BsonDocument()), new BuildInfoResult(new BsonDocument("version", "2.6.3"))))); _subject = new BinaryConnection( serverId: serverId, endPoint: _endPoint, settings: new ConnectionSettings(), streamFactory: _streamFactory, connectionInitializer: _connectionInitializer, eventSubscriber: _capturedEvents); _stream = new BlockingMemoryStream(); _streamFactory.CreateStreamAsync(null, CancellationToken.None) .ReturnsForAnyArgs(Task.FromResult<Stream>(_stream)); _subject.OpenAsync(CancellationToken.None).Wait(); _capturedEvents.Clear(); _operationIdDisposer = EventContext.BeginOperation(); }
public BinaryConnection_CommandEventTests() { _capturedEvents = new EventCapturer() .Capture<CommandStartedEvent>() .Capture<CommandSucceededEvent>() .Capture<CommandFailedEvent>(); _mockStreamFactory = new Mock<IStreamFactory>(); _endPoint = new DnsEndPoint("localhost", 27017); var serverId = new ServerId(new ClusterId(), _endPoint); _mockConnectionInitializer = new Mock<IConnectionInitializer>(); _mockConnectionInitializer.Setup(i => i.InitializeConnectionAsync(It.IsAny<IConnection>(), CancellationToken.None)) .Returns(() => Task.FromResult(new ConnectionDescription( new ConnectionId(serverId), new IsMasterResult(new BsonDocument()), new BuildInfoResult(new BsonDocument("version", "2.6.3"))))); _subject = new BinaryConnection( serverId: serverId, endPoint: _endPoint, settings: new ConnectionSettings(), streamFactory: _mockStreamFactory.Object, connectionInitializer: _mockConnectionInitializer.Object, eventSubscriber: _capturedEvents); _stream = new BlockingMemoryStream(); _mockStreamFactory.Setup(f => f.CreateStreamAsync(_endPoint, CancellationToken.None)) .Returns(Task.FromResult<Stream>(_stream)); _subject.OpenAsync(CancellationToken.None).Wait(); _capturedEvents.Clear(); _operationIdDisposer = EventContext.BeginOperation(); }
public BinaryConnection_CommandEventTests() { _capturedEvents = new EventCapturer() .Capture <CommandStartedEvent>() .Capture <CommandSucceededEvent>() .Capture <CommandFailedEvent>(); _mockStreamFactory = new Mock <IStreamFactory>(); _endPoint = new DnsEndPoint("localhost", 27017); var serverId = new ServerId(new ClusterId(), _endPoint); _mockConnectionInitializer = new Mock <IConnectionInitializer>(); _mockConnectionInitializer.Setup(i => i.InitializeConnectionAsync(It.IsAny <IConnection>(), CancellationToken.None)) .Returns(() => Task.FromResult(new ConnectionDescription( new ConnectionId(serverId), new IsMasterResult(new BsonDocument()), new BuildInfoResult(new BsonDocument("version", "2.6.3"))))); _subject = new BinaryConnection( serverId: serverId, endPoint: _endPoint, settings: new ConnectionSettings(), streamFactory: _mockStreamFactory.Object, connectionInitializer: _mockConnectionInitializer.Object, eventSubscriber: _capturedEvents); _stream = new BlockingMemoryStream(); _mockStreamFactory.Setup(f => f.CreateStreamAsync(_endPoint, CancellationToken.None)) .Returns(Task.FromResult <Stream>(_stream)); _subject.OpenAsync(CancellationToken.None).Wait(); _capturedEvents.Clear(); _operationIdDisposer = EventContext.BeginOperation(); }
public void Setup() { _capturedEvents = new EventCapturer() .Capture <CommandStartedEvent>() .Capture <CommandSucceededEvent>() .Capture <CommandFailedEvent>(); _streamFactory = Substitute.For <IStreamFactory>(); _endPoint = new DnsEndPoint("localhost", 27017); var serverId = new ServerId(new ClusterId(), _endPoint); _connectionInitializer = Substitute.For <IConnectionInitializer>(); _connectionInitializer.InitializeConnectionAsync(null, CancellationToken.None) .ReturnsForAnyArgs(Task.FromResult(new ConnectionDescription( new ConnectionId(serverId), new IsMasterResult(new BsonDocument()), new BuildInfoResult(new BsonDocument("version", "2.6.3"))))); _subject = new BinaryConnection( serverId: serverId, endPoint: _endPoint, settings: new ConnectionSettings(), streamFactory: _streamFactory, connectionInitializer: _connectionInitializer, eventSubscriber: _capturedEvents); _stream = new BlockingMemoryStream(); _streamFactory.CreateStreamAsync(null, CancellationToken.None) .ReturnsForAnyArgs(Task.FromResult <Stream>(_stream)); _subject.OpenAsync(CancellationToken.None).Wait(); _capturedEvents.Clear(); _operationIdDisposer = EventContext.BeginOperation(); }
public void Description_should_contain_expected_server_description() { var subject = CreateSubject(); subject.Description.Servers.Should().BeEmpty(); subject.Initialize(); _capturedEvents.Clear(); PublishDescription(_endPoint); var description = subject.Description; description.Servers.Should().ContainSingle(s => EndPointHelper.Equals(s.EndPoint, _endPoint) && s.State == ServerState.Connected); _capturedEvents.Next().Should().BeOfType <ClusterDescriptionChangedEvent>(); _capturedEvents.Any().Should().BeFalse(); }
public void ReceiveMessageAsync_should_complete_when_reply_is_already_on_the_stream() { using (var stream = new BlockingMemoryStream()) { _streamFactory.CreateStreamAsync(null, CancellationToken.None) .ReturnsForAnyArgs(Task.FromResult <Stream>(stream)); _subject.OpenAsync(CancellationToken.None).Wait(); _capturedEvents.Clear(); var messageToReceive = MessageHelper.BuildSuccessReply <BsonDocument>(new BsonDocument(), BsonDocumentSerializer.Instance, responseTo: 10); MessageHelper.WriteResponsesToStream(stream, new[] { messageToReceive }); var encoderSelector = new ReplyMessageEncoderSelector <BsonDocument>(BsonDocumentSerializer.Instance); var received = _subject.ReceiveMessageAsync(10, encoderSelector, _messageEncoderSettings, CancellationToken.None).Result; var expected = MessageHelper.TranslateMessagesToBsonDocuments(new[] { messageToReceive }); var actual = MessageHelper.TranslateMessagesToBsonDocuments(new[] { received }); actual.Should().BeEquivalentTo(expected); _capturedEvents.Next().Should().BeOfType <ConnectionReceivingMessageEvent>(); _capturedEvents.Next().Should().BeOfType <ConnectionReceivedMessageEvent>(); _capturedEvents.Any().Should().BeFalse(); } }
public void RequestHeartbeat_should_force_another_heartbeat() { SetupHeartbeatConnection(); _subject.Initialize(); SpinWait.SpinUntil(() => _subject.Description.State == ServerState.Connected, TimeSpan.FromSeconds(5)).Should().BeTrue(); _capturedEvents.Clear(); _subject.RequestHeartbeat(); // the next requests down heartbeat connection will fail, so the state should // go back to disconnected SpinWait.SpinUntil(() => _subject.Description.State == ServerState.Disconnected, TimeSpan.FromSeconds(5)).Should().BeTrue(); // when heart fails, we immediately attempt a second, hence the multiple events... _capturedEvents.Next().Should().BeOfType <ServerHeartbeatStartedEvent>(); _capturedEvents.Next().Should().BeOfType <ServerHeartbeatFailedEvent>(); _capturedEvents.Next().Should().BeOfType <ServerHeartbeatStartedEvent>(); _capturedEvents.Next().Should().BeOfType <ServerHeartbeatFailedEvent>(); _capturedEvents.Any().Should().BeFalse(); }
public void ReceiveMessage_should_complete_when_reply_is_already_on_the_stream( [Values(false, true)] bool async) { using (var stream = new BlockingMemoryStream()) { var messageToReceive = MessageHelper.BuildReply <BsonDocument>(new BsonDocument(), BsonDocumentSerializer.Instance, responseTo: 10); MessageHelper.WriteResponsesToStream(stream, new[] { messageToReceive }); var encoderSelector = new ReplyMessageEncoderSelector <BsonDocument>(BsonDocumentSerializer.Instance); ResponseMessage received; if (async) { _mockStreamFactory.Setup(f => f.CreateStreamAsync(_endPoint, CancellationToken.None)) .Returns(Task.FromResult <Stream>(stream)); _subject.OpenAsync(CancellationToken.None).GetAwaiter().GetResult(); _capturedEvents.Clear(); received = _subject.ReceiveMessageAsync(10, encoderSelector, _messageEncoderSettings, CancellationToken.None).GetAwaiter().GetResult(); } else { _mockStreamFactory.Setup(f => f.CreateStream(_endPoint, CancellationToken.None)) .Returns(stream); _subject.Open(CancellationToken.None); _capturedEvents.Clear(); received = _subject.ReceiveMessage(10, encoderSelector, _messageEncoderSettings, CancellationToken.None); } var expected = MessageHelper.TranslateMessagesToBsonDocuments(new[] { messageToReceive }); var actual = MessageHelper.TranslateMessagesToBsonDocuments(new[] { received }); actual.Should().BeEquivalentTo(expected); _capturedEvents.Next().Should().BeOfType <ConnectionReceivingMessageEvent>(); _capturedEvents.Next().Should().BeOfType <ConnectionReceivedMessageEvent>(); _capturedEvents.Any().Should().BeFalse(); } }
public void AcquireConnection_should_return_a_connection( [Values(false, true)] bool async) { InitializeAndWait(); _capturedEvents.Clear(); IConnectionHandle connection; if (async) { connection = _subject.AcquireConnectionAsync(CancellationToken.None).GetAwaiter().GetResult(); } else { connection = _subject.AcquireConnection(CancellationToken.None); } connection.Should().NotBeNull(); _subject.AvailableCount.Should().Be(_settings.MaxConnections - 1); _subject.CreatedCount.Should().Be(_settings.MinConnections); _subject.DormantCount.Should().Be(_settings.MinConnections - 1); _subject.UsedCount.Should().Be(1); _capturedEvents.Next().Should().BeOfType <ConnectionPoolCheckingOutConnectionEvent>(); _capturedEvents.Next().Should().BeOfType <ConnectionPoolCheckedOutConnectionEvent>(); _capturedEvents.Any().Should().BeFalse(); }
public async Task Ensure_server_session_are_allocated_only_on_connection_checkout() { var eventCapturer = new EventCapturer() .Capture <ConnectionPoolCheckedOutConnectionEvent>() .Capture <CommandStartedEvent>(); using var client = DriverTestConfiguration.CreateDisposableClient( (MongoClientSettings settings) => { settings.MaxConnectionPoolSize = 1; settings.ClusterConfigurator = c => c.Subscribe(eventCapturer); }, logger: null); var database = client.GetDatabase("test"); var collection = database.GetCollection <BsonDocument>("inventory"); database.DropCollection("inventory"); collection.InsertOne(new BsonDocument("x", 0)); var serverSessionPool = (CoreServerSessionPool)Reflector.GetFieldValue(client.Cluster, "_serverSessionPool"); var serverSessionsList = (List <ICoreServerSession>)Reflector.GetFieldValue(serverSessionPool, "_pool"); var serverSession = serverSessionsList.Single(); eventCapturer.Clear(); var eventsTask = eventCapturer.NotifyWhen(events => { var connectionCheckedOutEvent = events.OfType <ConnectionPoolCheckedOutConnectionEvent>().FirstOrDefault(); var commandStartedEvent = events.OfType <CommandStartedEvent>().FirstOrDefault(); if (commandStartedEvent.ConnectionId != null) { serverSessionsList.Count.Should().Be(0); commandStartedEvent.Command["lsid"].Should().Be(serverSession.Id); return(true); } else if (connectionCheckedOutEvent.ConnectionId != null) { serverSessionsList.Single().Should().Be(serverSession); } return(false); }); collection.InsertOne(new BsonDocument("x", 1)); await TasksUtils.WithTimeout(eventsTask, 1000); }
public void Connection_pool_should_not_be_cleared_when_replSetStepDown_and_GetMore([Values(false, true)] bool async) { RequireServer.Check().Supports(Feature.KeepConnectionPoolWhenReplSetStepDown).ClusterType(ClusterType.ReplicaSet); var eventCapturer = new EventCapturer().Capture <ConnectionPoolRemovedConnectionEvent>(); using (var client = CreateDisposableClient(eventCapturer)) { var database = client.GetDatabase(_databaseName, new MongoDatabaseSettings { WriteConcern = WriteConcern.WMajority }); database.DropCollection(_databaseName); var collection = database.GetCollection <BsonDocument>(_collectionName, new MongoCollectionSettings { WriteConcern = WriteConcern.WMajority }); var adminDatabase = client.GetDatabase("admin").WithWriteConcern(WriteConcern.W1); collection.InsertMany( new[] { new BsonDocument("x", 1), new BsonDocument("x", 2), new BsonDocument("x", 3), new BsonDocument("x", 4), new BsonDocument("x", 5), }); eventCapturer.Clear(); var cursor = collection.FindSync(FilterDefinition <BsonDocument> .Empty, new FindOptions <BsonDocument> { BatchSize = 2 }); cursor.MoveNext(); BsonDocument replSetStepDownResult; if (async) { replSetStepDownResult = adminDatabase.RunCommandAsync <BsonDocument>("{ replSetStepDown : 5, force : true }").GetAwaiter().GetResult(); } else { replSetStepDownResult = adminDatabase.RunCommand <BsonDocument>("{ replSetStepDown : 5, force : true }"); } replSetStepDownResult.GetValue("ok", false).ToBoolean().Should().BeTrue(); cursor.MoveNext(); eventCapturer.Events.Should().BeEmpty(); } }
public void BulkWrite_should_pin_connection_as_expected( [Values(1, 3)] int attempts, [Values(false, true)] bool async) { SkipIfNotLoadBalancingMode(); KillOpenTransactions(); SetupData(); ServiceIdHelper.IsServiceIdEmulationEnabled = true; // TODO: temporary solution to enable emulating serviceId in a server response var eventCapturer = new EventCapturer() .Capture <ConnectionPoolCheckedOutConnectionEvent>() .Capture <ConnectionPoolCheckingOutConnectionEvent>() .Capture <ConnectionPoolCheckedInConnectionEvent>() .Capture <ConnectionPoolCheckingInConnectionEvent>() .Capture <CommandSucceededEvent>(); using (var cluster = CreateLoadBalancedCluster(eventCapturer)) { eventCapturer.Clear(); for (int i = 1; i <= attempts; i++) { ICoreSessionHandle session; DisposableBindingBundle <IReadWriteBindingHandle, RetryableWriteContext> writeBindingsBundle; using (session = CreateSession(cluster, isImplicit: false, withTransaction: true)) { eventCapturer.Any().Should().BeFalse(); using (writeBindingsBundle = CreateReadWriteBindingsAndRetryableWriteContext(cluster, session.Fork(), async)) { AssertCheckOutOnlyEvents(eventCapturer, i); _ = CreateAndRunBulkOperation(writeBindingsBundle.RetryableContext, async); AssertCommand(eventCapturer, "insert", noMoreEvents: true); } } AssertCommand(eventCapturer, "abortTransaction", noMoreEvents: false); AssertCheckInOnlyEvents(eventCapturer); AssertSessionReferenceCount(session, 0); AssertChannelReferenceCount(writeBindingsBundle.RetryableContext.Channel, 0); } } }
public void RequestHeartbeat_should_force_another_heartbeat() { var capturedEvents = new EventCapturer(); var subject = CreateSubject(out var mockConnection, out _, out _, capturedEvents); SetupHeartbeatConnection(mockConnection); subject.Initialize(); SpinWait.SpinUntil(() => subject.Description.State == ServerState.Connected, TimeSpan.FromSeconds(5)).Should().BeTrue(); capturedEvents.Clear(); subject.RequestHeartbeat(); // the next requests down heartbeat connection will fail, so the state should // go back to disconnected SpinWait.SpinUntil(() => subject.Description.State == ServerState.Disconnected, TimeSpan.FromSeconds(5)).Should().BeTrue(); capturedEvents.Next().Should().BeOfType <ServerHeartbeatStartedEvent>(); capturedEvents.Next().Should().BeOfType <ServerHeartbeatFailedEvent>(); capturedEvents.Any().Should().BeFalse(); }
public void Heartbeat_should_work_as_expected() { var heartbeatSuceededTimestamps = new ConcurrentQueue <DateTime>(); var eventCapturer = new EventCapturer() .Capture <ServerHeartbeatSucceededEvent>( (@event) => { heartbeatSuceededTimestamps.Enqueue(DateTime.UtcNow); return(true); } ); var heartbeatInterval = TimeSpan.FromMilliseconds(500); eventCapturer.Clear(); using (var client = CreateClient(eventCapturer, heartbeatInterval)) { eventCapturer.WaitForOrThrowIfTimeout( events => events.Count() > 3, // wait for at least 3 events TimeSpan.FromSeconds(10), (timeout) => { return($"Waiting for the expected events exceeded the timeout {timeout}. The number of triggered events is {eventCapturer.Events.ToList().Count}."); }); } var heartbeatSuceededTimestampsList = heartbeatSuceededTimestamps.ToList(); // we have at least 3 items here // Skip the first event because we have nothing to compare it to for (int i = 1; i < heartbeatSuceededTimestampsList.Count; i++) { var attemptDuration = heartbeatSuceededTimestampsList[i] - heartbeatSuceededTimestampsList[i - 1]; attemptDuration .Should() .BeLessThan(TimeSpan.FromSeconds(2)); // Assert the client processes isMaster replies more frequently than 10 secs (approximately every 500ms) } }
public void AcquireConnectionAsync_should_return_a_connection() { InitializeAndWait(); _capturedEvents.Clear(); var connection = _subject.AcquireConnectionAsync(CancellationToken.None).Result; connection.Should().NotBeNull(); _subject.AvailableCount.Should().Be(_settings.MaxConnections - 1); _subject.CreatedCount.Should().Be(_settings.MinConnections); _subject.DormantCount.Should().Be(_settings.MinConnections - 1); _subject.UsedCount.Should().Be(1); _capturedEvents.Next().Should().BeOfType <ConnectionPoolCheckingOutConnectionEvent>(); _capturedEvents.Next().Should().BeOfType <ConnectionPoolCheckedOutConnectionEvent>(); _capturedEvents.Any().Should().BeFalse(); }
public void AcquireConnection_should_throw_an_InvalidOperationException_if_not_initialized( [Values(false, true)] bool async) { _capturedEvents.Clear(); Action act; if (async) { act = () => _subject.AcquireConnectionAsync(CancellationToken.None).GetAwaiter().GetResult(); } else { act = () => _subject.AcquireConnection(CancellationToken.None); } act.ShouldThrow <InvalidOperationException>(); _capturedEvents.Next().Should().BeOfType <ConnectionPoolCheckingOutConnectionEvent>(); var connectionPoolCheckingOutConnectionFailedEvent = _capturedEvents.Next(); var e = connectionPoolCheckingOutConnectionFailedEvent.Should().BeOfType <ConnectionPoolCheckingOutConnectionFailedEvent>().Subject; e.Reason.Should().Be(ConnectionCheckOutFailedReason.ConnectionError); _capturedEvents.Any().Should().BeFalse(); }
public void Connection_pool_should_not_be_cleared_when_replSetStepDown_and_GetMore([Values(false, true)] bool async) { RequireServer.Check().Supports(Feature.KeepConnectionPoolWhenReplSetStepDown).ClusterType(ClusterType.ReplicaSet); var eventCapturer = new EventCapturer().Capture <ConnectionPoolClearedEvent>(); using (var client = CreateDisposableClient(eventCapturer)) { var database = client.GetDatabase(_databaseName, new MongoDatabaseSettings { WriteConcern = WriteConcern.WMajority }); database.DropCollection(_databaseName); var collection = database.GetCollection <BsonDocument>(_collectionName, new MongoCollectionSettings { WriteConcern = WriteConcern.WMajority }); var adminDatabase = client.GetDatabase("admin").WithWriteConcern(WriteConcern.W1); collection.InsertMany( new[] { new BsonDocument("x", 1), new BsonDocument("x", 2), new BsonDocument("x", 3), new BsonDocument("x", 4), new BsonDocument("x", 5), }); eventCapturer.Clear(); var cursor = collection.FindSync(FilterDefinition <BsonDocument> .Empty, new FindOptions <BsonDocument> { BatchSize = 2 }); cursor.MoveNext(); foreach (var secondary in client.Cluster.Description.Servers.Where(c => c.Type == ServerType.ReplicaSetSecondary)) { RunOnSecondary(client, secondary.EndPoint, BsonDocument.Parse("{ replSetFreeze : 0 }")); } var replSetStepDownCommand = BsonDocument.Parse("{ replSetStepDown : 30, force : true }"); BsonDocument replSetStepDownResult; if (async) { replSetStepDownResult = adminDatabase.RunCommandAsync <BsonDocument>(replSetStepDownCommand).GetAwaiter().GetResult(); } else { replSetStepDownResult = adminDatabase.RunCommand <BsonDocument>(replSetStepDownCommand); } replSetStepDownResult.Should().NotBeNull(); replSetStepDownResult.GetValue("ok", false).ToBoolean().Should().BeTrue(); cursor.MoveNext(); eventCapturer.Events.Should().BeEmpty(); // it also means that no new PoolClearedEvent } void RunOnSecondary(IMongoClient primaryClient, EndPoint secondaryEndpoint, BsonDocument command) { var secondarySettings = primaryClient.Settings.Clone(); secondarySettings.ClusterConfigurator = null; #pragma warning disable CS0618 // Type or member is obsolete secondarySettings.ConnectionMode = ConnectionMode.Direct; #pragma warning restore CS0618 // Type or member is obsolete var secondaryDnsEndpoint = (DnsEndPoint)secondaryEndpoint; secondarySettings.Server = new MongoServerAddress(secondaryDnsEndpoint.Host, secondaryDnsEndpoint.Port); using (var secondaryClient = DriverTestConfiguration.CreateDisposableClient(secondarySettings)) { var adminDatabase = secondaryClient.GetDatabase(DatabaseNamespace.Admin.DatabaseName); adminDatabase.RunCommand <BsonDocument>(command); } } }
public async Task PoolClearedError_write_retryablity_test([Values(false, true)] bool async) { RequireServer.Check() .Supports(Feature.FailPointsBlockConnection) .ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded); var heartbeatInterval = TimeSpan.FromMilliseconds(50); var eventsWaitTimeout = TimeSpan.FromMilliseconds(5000); var failPointCommand = BsonDocument.Parse( $@"{{ configureFailPoint : 'failCommand', mode : {{ 'times' : 1 }}, data : {{ failCommands : [ 'insert' ], errorCode : 91, blockConnection: true, blockTimeMS: 1000, errorLabels: [""RetryableWriteError""] }} }}"); IServerSelector failPointSelector = WritableServerSelector.Instance; var settings = DriverTestConfiguration.GetClientSettings(); if (CoreTestConfiguration.Cluster.Description.Type == ClusterType.Sharded) { var serverAddress = settings.Servers.First(); settings.Servers = new[] { serverAddress }; // set settings.DirectConnection = true after removing obsolete ConnectionMode #pragma warning disable CS0618 // Type or member is obsolete settings.ConnectionMode = ConnectionMode.Direct; #pragma warning restore CS0618 // Type or member is obsolete failPointSelector = new EndPointServerSelector(new DnsEndPoint(serverAddress.Host, serverAddress.Port)); } settings.MaxConnectionPoolSize = 1; settings.RetryWrites = true; var eventCapturer = new EventCapturer() .Capture <ConnectionPoolClearedEvent>() .Capture <ConnectionPoolCheckedOutConnectionEvent>() .Capture <ConnectionPoolCheckingOutConnectionFailedEvent>() .CaptureCommandEvents("insert"); var failpointServer = DriverTestConfiguration.Client.Cluster.SelectServer(failPointSelector, default); using var failPoint = FailPoint.Configure(failpointServer, NoCoreSession.NewHandle(), failPointCommand); using var client = CreateClient(settings, eventCapturer, heartbeatInterval); var database = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName); var collection = database.GetCollection <BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName); eventCapturer.Clear(); if (async) { await ThreadingUtilities.ExecuteTasksOnNewThreads(2, async _ => { await collection.InsertOneAsync(new BsonDocument("x", 1)); }); } else { ThreadingUtilities.ExecuteOnNewThreads(2, _ => { collection.InsertOne(new BsonDocument("x", 1)); }); } // wait for 2 CommandSucceededEvent events, meaning that all other events should be received eventCapturer.WaitForOrThrowIfTimeout( events => events.OfType <CommandSucceededEvent>().Count() == 2, eventsWaitTimeout); eventCapturer.Events.OfType <CommandStartedEvent>().Count().Should().Be(3); eventCapturer.Events.OfType <CommandFailedEvent>().Count().Should().Be(1); eventCapturer.Events.OfType <CommandSucceededEvent>().Count().Should().Be(2); eventCapturer.Events.OfType <ConnectionPoolClearedEvent>().Count().Should().Be(1); eventCapturer.Events.OfType <ConnectionPoolCheckedOutConnectionEvent>().Count().Should().Be(3); eventCapturer.Events.OfType <ConnectionPoolCheckingOutConnectionFailedEvent>().Count().Should().Be(1); }
public void Should_discover_all_servers_in_the_cluster_reported_by_the_primary() { _settings = _settings.With(endPoints: new[] { _firstEndPoint }); var subject = CreateSubject(); subject.Initialize(); _capturedEvents.Clear(); PublishDescription(_firstEndPoint, ServerType.ReplicaSetPrimary); var description = subject.Description; description.State.Should().Be(ClusterState.Connected); description.Type.Should().Be(ClusterType.ReplicaSet); description.Servers.Should().BeEquivalentTo(GetDescriptions(_firstEndPoint, _secondEndPoint, _thirdEndPoint)); _capturedEvents.Next().Should().BeOfType <ClusterAddingServerEvent>(); _capturedEvents.Next().Should().BeOfType <ClusterAddedServerEvent>(); _capturedEvents.Next().Should().BeOfType <ClusterAddingServerEvent>(); _capturedEvents.Next().Should().BeOfType <ClusterAddedServerEvent>(); _capturedEvents.Next().Should().BeOfType <ClusterDescriptionChangedEvent>(); _capturedEvents.Any().Should().BeFalse(); }
public void BsonSizeLimitAndBatchSizeSplittingTest( [Values(false, true)] bool async) { RequireServer.Check().Supports(Feature.ClientSideEncryption); var eventCapturer = new EventCapturer().Capture <CommandStartedEvent>(e => e.CommandName == "insert"); using (var client = ConfigureClient()) using (var clientEncrypted = ConfigureClientEncrypted(kmsProviderFilter: "local", eventCapturer: eventCapturer)) { var collLimitSchema = JsonFileReader.Instance.Documents["limits.limits-schema.json"]; CreateCollection(client, __collCollectionNamespace, new BsonDocument("$jsonSchema", collLimitSchema)); var datakeysLimitsKey = JsonFileReader.Instance.Documents["limits.limits-key.json"]; var keyVaultCollection = GetCollection(client, __keyVaultCollectionNamespace); Insert(keyVaultCollection, async, datakeysLimitsKey); var coll = GetCollection(clientEncrypted, __collCollectionNamespace); var exception = Record.Exception( () => Insert( coll, async, new BsonDocument { { "_id", "over_2mib_under_16mib" }, { "unencrypted", new string('a', 2097152) } })); exception.Should().BeNull(); eventCapturer.Clear(); var limitsDoc = JsonFileReader.Instance.Documents["limits.limits-doc.json"]; limitsDoc.AddRange( new BsonDocument { { "_id", "encryption_exceeds_2mib" }, { "unencrypted", new string('a', 2097152 - 2000) } }); exception = Record.Exception( () => Insert( coll, async, limitsDoc)); exception.Should().BeNull(); eventCapturer.Clear(); exception = Record.Exception( () => Insert( coll, async, new BsonDocument { { "_id", "over_2mib_1" }, { "unencrypted", new string('a', 2097152) } }, new BsonDocument { { "_id", "over_2mib_2" }, { "unencrypted", new string('a', 2097152) } })); exception.Should().BeNull(); eventCapturer.Count.Should().Be(2); eventCapturer.Clear(); var limitsDoc1 = JsonFileReader.Instance.Documents["limits.limits-doc.json"]; limitsDoc1.AddRange( new BsonDocument { { "_id", "encryption_exceeds_2mib_1" }, { "unencrypted", new string('a', 2097152 - 2000) } }); var limitsDoc2 = JsonFileReader.Instance.Documents["limits.limits-doc.json"]; limitsDoc2.AddRange( new BsonDocument { { "_id", "encryption_exceeds_2mib_2" }, { "unencrypted", new string('a', 2097152 - 2000) } }); exception = Record.Exception( () => Insert( coll, async, limitsDoc1, limitsDoc2)); exception.Should().BeNull(); eventCapturer.Count.Should().Be(2); eventCapturer.Clear(); exception = Record.Exception( () => Insert( coll, async, new BsonDocument { { "_id", "under_16mib" }, { "unencrypted", new string('a', 16777216 - 2000) } })); exception.Should().BeNull(); eventCapturer.Clear(); limitsDoc = JsonFileReader.Instance.Documents["limits.limits-doc.json"]; limitsDoc.AddRange( new BsonDocument { { "_id", "encryption_exceeds_16mib" }, { "unencrypted", new string('a', 16777216 - 2000) } }); exception = Record.Exception( () => Insert( coll, async, limitsDoc)); exception.Should().NotBeNull(); eventCapturer.Clear(); // additional not spec tests exception = Record.Exception( () => Insert( coll, async, new BsonDocument { { "_id", "advanced_over_2mib_1" }, { "unencrypted", new string('a', 2097152) } }, new BsonDocument { { "_id", "advanced_over_2mib_2" }, { "unencrypted", new string('a', 2097152) } }, new BsonDocument { { "_id", "advanced_over_2mib_3" }, { "unencrypted", new string('a', 2097152) } })); exception.Should().BeNull(); eventCapturer.Count.Should().Be(3); eventCapturer.Clear(); exception = Record.Exception( () => Insert( coll, async, new BsonDocument { { "_id", "small_1" }, { "unencrypted", "a" } }, new BsonDocument { { "_id", "small_2" }, { "unencrypted", "a" } }, new BsonDocument { { "_id", "small_3" }, { "unencrypted", "a" } })); exception.Should().BeNull(); eventCapturer.Count.Should().Be(1); eventCapturer.Clear(); } }
public async Task Ensure_server_session_are_allocated_only_on_connection_checkout([Values(true, false)] bool async) { var eventCapturer = new EventCapturer() .Capture <CommandStartedEvent>(); using var client = DriverTestConfiguration.CreateDisposableClient( (MongoClientSettings settings) => { settings.RetryWrites = true; settings.MaxConnectionPoolSize = 1; settings.ClusterConfigurator = c => c.Subscribe(eventCapturer); }, logger: null); var database = client.GetDatabase("test"); database.DropCollection("inventory"); var collection = database.GetCollection <BsonDocument>("inventory"); const int operationsCount = 8; var singleSessionUsed = false; for (int i = 0; i < 5; i++) { eventCapturer.Clear(); await ThreadingUtilities.ExecuteTasksOnNewThreads(operationsCount, async i => { switch (i) { case 0: if (async) { await collection.InsertOneAsync(new BsonDocument("x", 0)); } else { collection.InsertOne(new BsonDocument("x", 0)); } break; case 1: if (async) { await collection.DeleteOneAsync(Builders <BsonDocument> .Filter.Eq("_id", 1)); } else { collection.DeleteOne(Builders <BsonDocument> .Filter.Eq("_id", 1)); } break; case 2: if (async) { await collection.UpdateOneAsync(Builders <BsonDocument> .Filter.Empty, Builders <BsonDocument> .Update.Set("a", 1)); } else { collection.UpdateOne(Builders <BsonDocument> .Filter.Empty, Builders <BsonDocument> .Update.Set("a", 1)); } break; case 3: var bulkWriteRequests = new WriteModel <BsonDocument>[] { new UpdateOneModel <BsonDocument>(Builders <BsonDocument> .Filter.Empty, new BsonDocument("$set", new BsonDocument("1", 1))) }; if (async) { await collection.BulkWriteAsync(bulkWriteRequests); } else { collection.BulkWrite(bulkWriteRequests); } break; case 4: if (async) { await collection.FindOneAndDeleteAsync(Builders <BsonDocument> .Filter.Empty); } else { collection.FindOneAndDelete(Builders <BsonDocument> .Filter.Empty); } break; case 5: if (async) { await collection.FindOneAndUpdateAsync(Builders <BsonDocument> .Filter.Empty, Builders <BsonDocument> .Update.Set("a", 1)); } else { collection.FindOneAndUpdate(Builders <BsonDocument> .Filter.Empty, Builders <BsonDocument> .Update.Set("a", 1)); } break; case 6: if (async) { await collection.FindOneAndReplaceAsync(Builders <BsonDocument> .Filter.Empty, new BsonDocument("x", 0)); } else { collection.FindOneAndReplace(Builders <BsonDocument> .Filter.Empty, new BsonDocument("x", 0)); } break; case 7: if (async) { var cursor = await collection.FindAsync(Builders <BsonDocument> .Filter.Empty); _ = await cursor.ToListAsync(); } else { _ = collection.Find(Builders <BsonDocument> .Filter.Empty).ToList(); } break; } }); eventCapturer.WaitForOrThrowIfTimeout(e => e.OfType <CommandStartedEvent>().Count() >= operationsCount, TimeSpan.FromSeconds(10)); var lsids = eventCapturer.Events.OfType <CommandStartedEvent>().Select(c => c.Command["lsid"]).ToArray(); var distinctLsidsCount = lsids.Distinct().Count(); distinctLsidsCount.Should().BeLessThan(operationsCount); if (distinctLsidsCount == 1) { singleSessionUsed = true; break; } } singleSessionUsed.Should().BeTrue("At least one iteration should use single session"); }
private (IConnectionPool, FailPoint, ICluster, Func <object, bool>) SetupConnectionData(BsonDocument test, EventCapturer eventCapturer, bool isUnit) { ParseSettings(test, out var connectionPoolSettings, out var connectionSettings); IConnectionPool connectionPool; ICluster cluster = null; FailPoint failPoint = null; Func <object, bool> eventsFilter = _ => true; if (isUnit) { var endPoint = new DnsEndPoint("localhost", 27017); var serverId = new ServerId(new ClusterId(), endPoint); var connectionFactory = new Mock <IConnectionFactory>(); var connectionExceptionHandler = new Mock <IConnectionExceptionHandler>(); connectionFactory .Setup(c => c.CreateConnection(serverId, endPoint)) .Returns(() => { var connection = new MockConnection(serverId, connectionSettings, eventCapturer); return(connection); }); connectionPool = new ExclusiveConnectionPool( serverId, endPoint, connectionPoolSettings, connectionFactory.Object, eventCapturer, connectionExceptionHandler.Object); connectionPool.Initialize(); } else { var async = test.GetValue(Schema.async).ToBoolean(); cluster = CoreTestConfiguration.CreateCluster(b => b .ConfigureServer(s => s.With( heartbeatInterval: TimeSpan.FromMinutes(10))) .ConfigureConnectionPool(c => c.With( maxConnecting: connectionPoolSettings.MaxConnecting, maxConnections: connectionPoolSettings.MaxConnections, minConnections: connectionPoolSettings.MinConnections, maintenanceInterval: connectionPoolSettings.MaintenanceInterval, waitQueueTimeout: connectionPoolSettings.WaitQueueTimeout)) .ConfigureConnection(s => s.With(applicationName: $"{connectionSettings.ApplicationName}_async_{async}")) .Subscribe(eventCapturer)); var server = cluster.SelectServer(WritableServerSelector.Instance, CancellationToken.None); connectionPool = server._connectionPool(); if (test.TryGetValue(Schema.Intergration.failPoint, out var failPointDocument)) { if (failPointDocument.AsBsonDocument.Contains("data")) { var data = failPointDocument["data"].AsBsonDocument; if (data.TryGetValue("appName", out var appNameValue)) { data["appName"] = $"{appNameValue}_async_{async}"; } } var resetPool = connectionPoolSettings.MinConnections > 0; if (resetPool) { eventCapturer.WaitForOrThrowIfTimeout(events => events.Any(e => e is ConnectionCreatedEvent), TimeSpan.FromMilliseconds(500)); var connectionIdsToIgnore = new HashSet <int>(eventCapturer.Events .OfType <ConnectionCreatedEvent>() .Select(c => c.ConnectionId.LocalValue) .ToList()); eventsFilter = o => { if (o is ConnectionOpenedEvent or ConnectionClosedEvent or ConnectionCreatedEvent or ConnectionFailedEvent) { var connectionId = o.ConnectionId(); return(!connectionIdsToIgnore.Contains(connectionId.LocalValue) && EndPointHelper.Equals(connectionId.ServerId.EndPoint, server.EndPoint)); } if (o is ConnectionPoolReadyEvent or ConnectionPoolClearedEvent) { var serverId = o.ServerId(); return(EndPointHelper.Equals(serverId.EndPoint, server.EndPoint)); } return(true); }; connectionPool.Clear(closeInUseConnections: false); eventCapturer.WaitForOrThrowIfTimeout(events => events.Any(e => e is ConnectionPoolClearedEvent), TimeSpan.FromMilliseconds(500)); } var failPointServer = CoreTestConfiguration.Cluster.SelectServer(new EndPointServerSelector(server.EndPoint), default); failPoint = FailPoint.Configure(failPointServer, NoCoreSession.NewHandle(), failPointDocument.AsBsonDocument); if (resetPool) { eventCapturer.Clear(); connectionPool.SetReady(); } } } return(connectionPool, failPoint, cluster, eventsFilter); }
public void Cursor_should_pin_connection_in_transaction_with_new_sessions_as_expected( [Values(1, 3)] 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>(); using (var cluster = CreateLoadBalancedCluster(eventCapturer)) { eventCapturer.Clear(); for (int i = 1; i <= attempts; i++) { ICoreSessionHandle session; DisposableBindingBundle <IReadBindingHandle, RetryableReadContext> readBindingsBundle; IAsyncCursor <BsonDocument> asyncCursor; using (session = CreateSession(cluster, isImplicit: false, withTransaction: true)) { AssertSessionReferenceCount(session, 1); eventCapturer.Any().Should().BeFalse(); using (readBindingsBundle = CreateReadBindingsAndRetryableReadContext(cluster, session.Fork(), async)) { AssertCheckOutOnlyEvents(eventCapturer, i); asyncCursor = CreateAndRunFindOperation(readBindingsBundle.RetryableContext, async); AssertCommand(eventCapturer, "find", noMoreEvents: true); } MoveNext(asyncCursor, async).Should().BeTrue(); // no op MoveNext(asyncCursor, async).Should().BeTrue(); AssertSessionReferenceCount(session, 2); AssertChannelReferenceCount(readBindingsBundle.RetryableContext.Channel, 2); AssertChannelReferenceCount(asyncCursor, 2); AssertCommand(eventCapturer, "getMore", noMoreEvents: true); if (forceCursorClose) { asyncCursor.Dispose(); AssertCommand(eventCapturer, "killCursors", noMoreEvents: true); } else { MoveNext(asyncCursor, async).Should().BeTrue(); // cursorId = 0 MoveNext(asyncCursor, async).Should().BeFalse(); AssertCommand(eventCapturer, "getMore", noMoreEvents: true); } } AssertCommand(eventCapturer, "abortTransaction", noMoreEvents: false); 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); } }
public void BulkWrite_and_cursor_should_share_pinned_connection_under_the_same_transaction_2( [Values(false, true)] bool async) { SkipIfNotLoadBalancingMode(); KillOpenTransactions(); SetupData(); var eventCapturer = new EventCapturer() .Capture <ConnectionPoolCheckedOutConnectionEvent>() .Capture <ConnectionPoolCheckingOutConnectionEvent>() .Capture <ConnectionPoolCheckedInConnectionEvent>() .Capture <ConnectionPoolCheckingInConnectionEvent>() .Capture <CommandSucceededEvent>(); using (var cluster = CreateLoadBalancedCluster(eventCapturer)) { eventCapturer.Clear(); ICoreSessionHandle session; DisposableBindingBundle <IReadWriteBindingHandle, RetryableWriteContext> writeBindingsBundle; DisposableBindingBundle <IReadBindingHandle, RetryableReadContext> readBindingsBundle; IAsyncCursor <BsonDocument> asyncCursor; using (session = CreateSession(cluster, isImplicit: false, withTransaction: true)) { AssertSessionReferenceCount(session, 1); eventCapturer.Any().Should().BeFalse(); // bulk operation using (writeBindingsBundle = CreateReadWriteBindingsAndRetryableWriteContext(cluster, session.Fork(), async)) { AssertCheckOutOnlyEvents(eventCapturer, 1); _ = CreateAndRunBulkOperation(writeBindingsBundle.RetryableContext, async); AssertCommand(eventCapturer, "insert", noMoreEvents: true); } AssertSessionReferenceCount(session, 1); // find operation using (readBindingsBundle = CreateReadBindingsAndRetryableReadContext(cluster, session.Fork(), async)) { eventCapturer.Any().Should().BeFalse(); asyncCursor = CreateAndRunFindOperation(readBindingsBundle.RetryableContext, async); AssertCommand(eventCapturer, "find", noMoreEvents: true); } } MoveNext(asyncCursor, async).Should().BeTrue(); // no op MoveNext(asyncCursor, async).Should().BeTrue(); // getMore AssertCommand(eventCapturer, "getMore", noMoreEvents: true); MoveNext(asyncCursor, async).Should().BeTrue(); // getMore AssertCommand(eventCapturer, "getMore", noMoreEvents: true); MoveNext(asyncCursor, async).Should().BeTrue(); // cursorId = 0 AssertCommand(eventCapturer, "getMore", noMoreEvents: false); AssertCommand(eventCapturer, "abortTransaction", noMoreEvents: false); AssertCheckInOnlyEvents(eventCapturer); MoveNext(asyncCursor, async).Should().BeFalse(); AssertChannelReferenceCount(readBindingsBundle.RetryableContext.Channel, 0); AssertChannelReferenceCount(writeBindingsBundle.RetryableContext.Channel, 0); AssertSessionReferenceCount(session, 0); } }
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); } }