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 AfterClusterTime_should_be_sent_after_the_first_read_operation() { RequireServer.Check().SupportsCausalConsistency(); var events = new EventCapturer() .Capture <CommandStartedEvent>(x => x.CommandName == "count") .Capture <CommandSucceededEvent>(x => x.CommandName == "find"); using (var client = GetClient(events)) using (var session = client.StartSession()) { client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection <BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName) .FindSync(session, FilterDefinition <BsonDocument> .Empty, new FindOptions <BsonDocument, BsonDocument> { Limit = 1 }); var commandSucceededEvent = (CommandSucceededEvent)events.Next(); session.OperationTime.Should().Be(commandSucceededEvent.Reply.GetValue("operationTime")); var operationTime = session.OperationTime; client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection <BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName) .Count(session, FilterDefinition <BsonDocument> .Empty); var commandStartedEvent = (CommandStartedEvent)events.Next(); commandStartedEvent.Command["readConcern"]["afterClusterTime"].AsBsonTimestamp.Should().Be(operationTime); } }
public void AfterClusterTime_should_be_sent_after_the_first_write_operation() { RequireServer.Check().SupportsCausalConsistency(); var events = new EventCapturer() .Capture <CommandStartedEvent>(x => x.CommandName == "count") .Capture <CommandSucceededEvent>(x => x.CommandName == "insert"); using (var client = GetClient(events)) using (var session = client.StartSession()) { client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection <BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName) .InsertOne(session, new BsonDocument("x", 1)); var commandSucceededEvent = (CommandSucceededEvent)events.Next(); session.OperationTime.Should().Be(commandSucceededEvent.Reply.GetValue("operationTime")); var operationTime = session.OperationTime; #pragma warning disable 618 client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection <BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName) .Count(session, FilterDefinition <BsonDocument> .Empty); #pragma warning restore var commandStartedEvent = (CommandStartedEvent)events.Next(); commandStartedEvent.Command["readConcern"]["afterClusterTime"].AsBsonTimestamp.Should().Be(operationTime); } }
public void Stop_should_trigger_immidiate_maintenace_call( [Values(false, true)] bool checkOutConnection, [Values(false, true)] bool closeInUseConnection) { var eventCapturer = new EventCapturer() .Capture <ConnectionPoolAddedConnectionEvent>() .Capture <ConnectionPoolRemovedConnectionEvent>(); int connectionId = 0; using (var pool = CreatePool( eventCapturer, minPoolSize: 1, connectionFactoryConfigurator: (factory) => { factory .Setup(f => f.CreateConnection(__serverId, __endPoint)) .Returns(() => new MockConnection(new ConnectionId(__serverId, ++connectionId), new ConnectionSettings(), eventCapturer)); })) // use to ensure that Maintenance attempt has been called { var subject = pool._maintenanceHelper(); var maitenanceInPlayTimeout = TimeSpan.FromMilliseconds(50); eventCapturer.WaitForEventOrThrowIfTimeout <ConnectionPoolAddedConnectionEvent>(maitenanceInPlayTimeout); eventCapturer.Next().Should().BeOfType <ConnectionPoolAddedConnectionEvent>().Which.ConnectionId.LocalValue.Should().Be(1); // minPoolSize has been enrolled eventCapturer.Any().Should().BeFalse(); SpinWait.SpinUntil(() => pool.ConnectionHolder._connections().Count > 0, TimeSpan.FromSeconds(1)).Should().BeTrue(); // wait until connection 1 has been returned to the pool after minPoolSize logic IConnection acquiredConnection = null; if (checkOutConnection) { acquiredConnection = pool.AcquireConnection(CancellationToken.None); acquiredConnection.ConnectionId.LocalValue.Should().Be(1); } IncrementGeneration(pool); subject.Stop(maxGenerationToReap: closeInUseConnection ? pool.Generation : null); var requestInPlayTimeout = TimeSpan.FromMilliseconds(100); if (!closeInUseConnection && checkOutConnection) { // connection in progress should be not touched Thread.Sleep(requestInPlayTimeout); } else { eventCapturer.WaitForOrThrowIfTimeout((events) => events.OfType <ConnectionPoolRemovedConnectionEvent>().Count() >= 1, requestInPlayTimeout); eventCapturer.Next().Should().BeOfType <ConnectionPoolRemovedConnectionEvent>(); } eventCapturer.Any().Should().BeFalse(); pool.AvailableCount.Should().Be(checkOutConnection ? pool.Settings.MaxConnections - 1 : pool.Settings.MaxConnections); pool.CreatedCount.Should().Be(checkOutConnection ? 1 : 0); pool.DormantCount.Should().Be(0); pool.PendingCount.Should().Be(0); pool.UsedCount.Should().Be(checkOutConnection ? 1 : 0); } }
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(); }
private void AssertCheckOutOnlyEvents(EventCapturer eventCapturer, int attempt, bool shouldHelloBeCalled, bool shouldNextAttemptTriggerCheckout = true) { if (attempt == 1 || shouldNextAttemptTriggerCheckout) { eventCapturer.Next().Should().BeOfType <ConnectionPoolCheckingOutConnectionEvent>(); if (shouldHelloBeCalled) // in other cases we will reuse the first connection { eventCapturer.Next().Should().BeOfType <CommandSucceededEvent>().Subject.CommandName.Should().Be(OppressiveLanguageConstants.LegacyHelloCommandName); } eventCapturer.Next().Should().BeOfType <ConnectionPoolCheckedOutConnectionEvent>(); } eventCapturer.Any().Should().BeFalse(); }
public void Command_should_update_the_session_and_cluster_cluster_times() { RequireServer.Check().VersionGreaterThanOrEqualTo("3.6").ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded); var eventCapturer = new EventCapturer().Capture <CommandSucceededEvent>(e => e.CommandName == "ping"); using (var cluster = CoreTestConfiguration.CreateCluster(b => b.Subscribe(eventCapturer))) using (var session = cluster.StartSession()) { var cancellationToken = CancellationToken.None; var server = (Server)cluster.SelectServer(WritableServerSelector.Instance, cancellationToken); using (var channel = server.GetChannel(cancellationToken)) { var command = BsonDocument.Parse("{ ping : 1 }"); channel.Command <BsonDocument>( session, ReadPreference.Primary, DatabaseNamespace.Admin, command, NoOpElementNameValidator.Instance, null, // additionalOptions () => CommandResponseHandling.Return, BsonDocumentSerializer.Instance, new MessageEncoderSettings(), cancellationToken); } var commandSucceededEvent = eventCapturer.Next().Should().BeOfType <CommandSucceededEvent>().Subject; var actualReply = commandSucceededEvent.Reply; var actualClusterTime = actualReply["$clusterTime"].AsBsonDocument; session.ClusterTime.Should().Be(actualClusterTime); server.ClusterClock.ClusterTime.Should().Be(actualClusterTime); } }
private void AssertEvents(EventCapturer actualEvents, BsonDocument test, Dictionary <string, BsonValue> sessionIdMap) { Logger.Debug("Asserting events"); if (test.Contains("expectations")) { var expectedEvents = test["expectations"].AsBsonArray.Cast <BsonDocument>().GetEnumerator(); while (actualEvents.Any()) { var actualEvent = actualEvents.Next(); if (!expectedEvents.MoveNext()) { throw new Exception($"Unexpected event of type: {actualEvent.GetType().Name}."); } var expectedEvent = expectedEvents.Current; RecursiveFieldSetter.SetAll(expectedEvent, "lsid", value => sessionIdMap[value.AsString]); AssertEvent(actualEvent, expectedEvent); } if (expectedEvents.MoveNext()) { var expectedEvent = expectedEvents.Current; throw new Exception($"Missing event: {expectedEvent}."); } } }
public void ReadConcern_should_include_level_when_not_using_the_server_default() { RequireServer.Check().SupportsCausalConsistency(); var events = new EventCapturer() .Capture <CommandStartedEvent>(x => x.CommandName == "count"); using (var client = GetClient(events)) using (var session = client.StartSession()) { client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection <BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName) .InsertOne(session, new BsonDocument("x", 1)); #pragma warning disable 618 client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection <BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName) .WithReadConcern(ReadConcern.Majority) .Count(session, FilterDefinition <BsonDocument> .Empty); #pragma warning restore var commandStartedEvent = (CommandStartedEvent)events.Next(); commandStartedEvent.Command["readConcern"].AsBsonDocument.Contains("level").Should().BeTrue(); commandStartedEvent.Command["readConcern"].AsBsonDocument.Contains("afterClusterTime").Should().BeTrue(); } }
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 <ConnectionPoolClearedEvent>() .Capture <ConnectionCreatedEvent>(); 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 exception = Record.Exception(() => { collection.InsertOne(new BsonDocument("test", 1)); }); var e = exception.Should().BeOfType <MongoNotPrimaryException>().Subject; e.Code.Should().Be(10107); if (shouldConnectionPoolBeCleared) { eventCapturer.Next().Should().BeOfType <ConnectionPoolClearedEvent>(); eventCapturer.Events.Should().BeEmpty(); } else { eventCapturer.Events.Should().BeEmpty(); } collection.InsertOne(new BsonDocument("test", 1)); if (shouldConnectionPoolBeCleared) { eventCapturer.Next().Should().BeOfType <ConnectionCreatedEvent>(); } eventCapturer.Events.Should().BeEmpty(); } } }
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(); }
protected virtual List <object> ExtractEventsForAsserting(EventCapturer eventCapturer) { var events = new List <object>(); while (eventCapturer.Any()) { events.Add(eventCapturer.Next()); } return(events); }
private List <CommandStartedEvent> GetEvents(EventCapturer eventCapturer) { var events = new List <CommandStartedEvent>(); while (eventCapturer.Any()) { events.Add((CommandStartedEvent)eventCapturer.Next()); } return(events); }
private void AssertEvents(EventCapturer actualEvents, BsonDocument test, Dictionary <string, BsonValue> sessionIdMap) { if (test.Contains("expectations")) { foreach (var expectedEvent in test["expectations"].AsBsonArray.Cast <BsonDocument>()) { RecursiveFieldSetter.SetAll(expectedEvent, "lsid", value => sessionIdMap[value.AsString]); var actualEvent = actualEvents.Next(); AssertEvent(actualEvent, expectedEvent); } } }
public void Session_OperationTime_should_get_updated_after_an_operation() { RequireServer.Check().SupportsCausalConsistency(); var events = new EventCapturer() .Capture <CommandStartedEvent>(x => x.CommandName == "count") .Capture <CommandSucceededEvent>(x => x.CommandName == "count"); using (var client = GetClient(events)) using (var session = client.StartSession()) { client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection <BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName) .Count(session, FilterDefinition <BsonDocument> .Empty); var commandStartedEvent = (CommandStartedEvent)events.Next(); commandStartedEvent.Command.GetValue("readConcern", null).Should().BeNull(); var commandSucceededEvent = (CommandSucceededEvent)events.Next(); session.OperationTime.Should().Be(commandSucceededEvent.Reply.GetValue("operationTime")); } }
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 <ConnectionPoolClearedEvent>() .Capture <ConnectionCreatedEvent>(); 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.Next().Should().BeOfType <ConnectionPoolClearedEvent>(); eventCapturer.Events.Should().BeEmpty(); collection.InsertOne(new BsonDocument("test", 1)); eventCapturer.Next().Should().BeOfType <ConnectionCreatedEvent>(); eventCapturer.Events.Should().BeEmpty(); } } }
public void Command_should_send_the_greater_of_the_session_and_cluster_cluster_times(long sessionTimestamp, long clusterTimestamp, long expectedTimestamp) { RequireServer.Check().VersionGreaterThanOrEqualTo("3.6").ClusterTypes(ClusterType.ReplicaSet, ClusterType.Sharded); var sessionClusterTime = new BsonDocument("clusterTime", new BsonTimestamp(sessionTimestamp)); var clusterClusterTime = new BsonDocument("clusterTime", new BsonTimestamp(clusterTimestamp)); var expectedClusterTime = new BsonDocument("clusterTime", new BsonTimestamp(expectedTimestamp)); var eventCapturer = new EventCapturer().Capture <CommandStartedEvent>(e => e.CommandName == "ping"); using (var cluster = CoreTestConfiguration.CreateCluster(b => b.Subscribe(eventCapturer))) using (var session = cluster.StartSession()) { var cancellationToken = CancellationToken.None; var server = (Server)cluster.SelectServer(WritableServerSelector.Instance, cancellationToken); using (var channel = server.GetChannel(cancellationToken)) { session.AdvanceClusterTime(sessionClusterTime); server.ClusterClock.AdvanceClusterTime(clusterClusterTime); var command = BsonDocument.Parse("{ ping : 1 }"); try { channel.Command <BsonDocument>( session, ReadPreference.Primary, DatabaseNamespace.Admin, command, null, // payloads NoOpElementNameValidator.Instance, null, // additionalOptions null, // postWriteAction CommandResponseHandling.Return, BsonDocumentSerializer.Instance, new MessageEncoderSettings(), cancellationToken); } catch (MongoCommandException ex) { // we're expecting the command to fail because the $clusterTime we sent is not properly signed // the point of this test is just to assert that the driver sent the higher of the session and cluster clusterTimes ex.Message.Should().Contain("Missing expected field \"signature\""); } } } var commandStartedEvent = eventCapturer.Next().Should().BeOfType <CommandStartedEvent>().Subject; var actualCommand = commandStartedEvent.Command; var actualClusterTime = actualCommand["$clusterTime"].AsBsonDocument; actualClusterTime.Should().Be(expectedClusterTime); }
// private methods private void AssertSessionIdWasNotSentIfUnacknowledgedWrite(EventCapturer eventCapturer, ICoreSessionHandle session, Exception ex) { if (session.IsImplicit) { var commandStartedEvent = (CommandStartedEvent)eventCapturer.Next(); var command = commandStartedEvent.Command; command.Contains("lsid").Should().BeFalse(); session.ReferenceCount().Should().Be(2); } else { var e = ex.Should().BeOfType <InvalidOperationException>().Subject; e.Message.Should().Be("Explicit session must not be used with unacknowledged writes."); } }
public void DescriptionChanged_should_be_raised_when_moving_from_disconnected_to_connected() { var changes = new List <ServerDescriptionChangedEventArgs>(); _subject.DescriptionChanged += (o, e) => changes.Add(e); SetupHeartbeatConnection(); _subject.Initialize(); SpinWait.SpinUntil(() => _subject.Description.State == ServerState.Connected, TimeSpan.FromSeconds(5)).Should().BeTrue(); changes.Count.Should().Be(1); changes[0].OldServerDescription.State.Should().Be(ServerState.Disconnected); changes[0].NewServerDescription.State.Should().Be(ServerState.Connected); _capturedEvents.Next().Should().BeOfType <ServerHeartbeatStartedEvent>(); _capturedEvents.Next().Should().BeOfType <ServerHeartbeatSucceededEvent>(); _capturedEvents.Any().Should().BeFalse(); }
private void AssertSessionIdWasSentWhenSupported(EventCapturer eventCapturer, ICoreSessionHandle session, Exception exception) { exception.Should().BeNull(); var commandStartedEvent = (CommandStartedEvent)eventCapturer.Next(); var command = commandStartedEvent.Command; if (session.Id == null) { command.Contains("lsid").Should().BeFalse(); } else { command["lsid"].Should().Be(session.Id); } session.ReferenceCount().Should().Be(2); }
public void TxnNumber_should_be_included_with_FindOneAndReplace() { RequireSupportForRetryableWrites(); var events = new EventCapturer().Capture <CommandStartedEvent>(x => x.CommandName == "findAndModify"); using (var client = GetClient(events)) using (var session = client.StartSession()) { client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection <BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName) .FindOneAndReplace("{x: 'asdfafsdf'}", new BsonDocument("x", 1)); var commandStartedEvent = (CommandStartedEvent)events.Next(); commandStartedEvent.Command.GetValue("txnNumber").Should().Be(new BsonInt64(1)); } }
public void AfterClusterTime_should_be_empty_on_the_first_operation() { RequireServer.Check().SupportsCausalConsistency(); var events = new EventCapturer().Capture <CommandStartedEvent>(x => x.CommandName == "count"); using (var client = GetClient(events)) using (var session = client.StartSession()) { #pragma warning disable 618 client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection <BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName) .Count(session, FilterDefinition <BsonDocument> .Empty); #pragma warning restore var commandStartedEvent = (CommandStartedEvent)events.Next(); commandStartedEvent.Command.GetValue("readConcern", null).Should().BeNull(); } }
public void AfterClusterTime_should_not_be_sent_when_the_session_is_not_causally_consistent() { RequireServer.Check().SupportsCausalConsistency(); var events = new EventCapturer() .Capture <CommandStartedEvent>(x => x.CommandName == "count"); using (var client = GetClient(events)) using (var session = client.StartSession(new ClientSessionOptions { CausalConsistency = false })) { client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName) .GetCollection <BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName) .Count(session, FilterDefinition <BsonDocument> .Empty); var commandStartedEvent = (CommandStartedEvent)events.Next(); commandStartedEvent.Command.Contains("readConcern").Should().BeFalse(); } }
protected void VerifySessionIdWasSentWhenSupported <TResult>( Func <WritableServerBinding, CancellationToken, Task <TResult> > executeAsync, Func <WritableServerBinding, CancellationToken, TResult> execute, string commandName, bool async) { var eventCapturer = new EventCapturer().Capture <CommandStartedEvent>(e => e.CommandName == commandName); using (var cluster = CoreTestConfiguration.CreateCluster(b => b.Subscribe(eventCapturer))) { using (var session = CoreTestConfiguration.StartSession(cluster)) using (var binding = new WritableServerBinding(cluster, session)) { var cancellationToken = new CancellationTokenSource().Token; if (async) { executeAsync(binding, cancellationToken).GetAwaiter().GetResult(); } else { execute(binding, cancellationToken); } var commandStartedEvent = (CommandStartedEvent)eventCapturer.Next(); var command = commandStartedEvent.Command; if (session.Id == null) { command.Contains("lsid").Should().BeFalse(); } else { command["lsid"].Should().Be(session.Id); } session.ReferenceCount().Should().Be(1); } } }
public void Heartbeat_should_make_immediate_next_attempt_for_streaming_protocol(string exceptionType, bool?moreToCome) { var capturedEvents = new EventCapturer() .Capture <ServerHeartbeatSucceededEvent>() .Capture <ServerHeartbeatFailedEvent>() .Capture <ServerDescriptionChangedEvent>(); var subject = CreateSubject(out var mockConnection, out _, out var mockRoundTimeTripMonitor, capturedEvents); subject.DescriptionChanged += (o, e) => { capturedEvents.TryGetEventHandler <ServerDescriptionChangedEvent>(out var eventHandler); eventHandler(new ServerDescriptionChangedEvent(e.OldServerDescription, e.NewServerDescription)); }; SetupHeartbeatConnection(mockConnection, isStreamable: true, autoFillStreamingResponses: false); Exception exception = null; switch (exceptionType) { case null: mockConnection.EnqueueCommandResponseMessage(CreateStreamableCommandResponseMessage(moreToCome.Value), null); break; case "MongoConnectionException": // previousDescription type is "Known" for this case mockConnection.EnqueueCommandResponseMessage( exception = CoreExceptionHelper.CreateException(exceptionType)); break; } // 10 seconds delay. Not expected to be processed mockConnection.EnqueueCommandResponseMessage(CreateStreamableCommandResponseMessage(), TimeSpan.FromSeconds(10)); subject.Initialize(); var expectedServerDescriptionChangedEventCount = exception != null ? 3 // +1 event because a connection initialized event doesn't have waiting : 2; capturedEvents.WaitForOrThrowIfTimeout( events => events.Count(e => e is ServerDescriptionChangedEvent) >= expectedServerDescriptionChangedEventCount, // the connection has been initialized and the first heatbeat event has been fired TimeSpan.FromSeconds(10)); capturedEvents.Next().Should().BeOfType <ServerDescriptionChangedEvent>(); // connection initialized AssertHeartbeatAttempt(); capturedEvents.Any().Should().BeFalse(); // the next attempt will be in 10 seconds because the second stremable respone has 10 seconds delay void AssertHeartbeatAttempt() { if (exception != null) { mockRoundTimeTripMonitor.Verify(c => c.Reset(), Times.Once); var serverHeartbeatFailedEvent = capturedEvents.Next().Should().BeOfType <ServerHeartbeatFailedEvent>().Subject; // updating the server based on the heartbeat serverHeartbeatFailedEvent.Exception.Should().Be(exception); var serverDescriptionChangedEvent = capturedEvents.Next().Should().BeOfType <ServerDescriptionChangedEvent>().Subject; serverDescriptionChangedEvent.NewDescription.HeartbeatException.Should().Be(exception); serverDescriptionChangedEvent = capturedEvents.Next().Should().BeOfType <ServerDescriptionChangedEvent>().Subject; // when we catch exceptions, we close the current connection, so opening connection will trigger one more ServerDescriptionChangedEvent serverDescriptionChangedEvent.OldDescription.HeartbeatException.Should().Be(exception); serverDescriptionChangedEvent.NewDescription.HeartbeatException.Should().BeNull(); } else { mockRoundTimeTripMonitor.Verify(c => c.Reset(), Times.Never); capturedEvents.Next().Should().BeOfType <ServerHeartbeatSucceededEvent>(); var serverDescriptionChangedEvent = capturedEvents.Next().Should().BeOfType <ServerDescriptionChangedEvent>().Subject; // updating the server based on the heartbeat serverDescriptionChangedEvent.NewDescription.HeartbeatException.Should().BeNull(); } } }
public void Command_should_use_serverApi([Values(false, true)] bool async) { RequireServer.Check().Supports(Feature.CommandMessage); var serverApi = new ServerApi(ServerApiVersion.V1); var eventCapturer = new EventCapturer().Capture <CommandStartedEvent>(e => e.CommandName == "ping"); var builder = CoreTestConfiguration .ConfigureCluster(new ClusterBuilder()) .Subscribe(eventCapturer) .ConfigureCluster(x => x.With(serverApi: serverApi)); using (var cluster = CoreTestConfiguration.CreateCluster(builder)) using (var session = cluster.StartSession()) { var cancellationToken = CancellationToken.None; var server = (Server)cluster.SelectServer(WritableServerSelector.Instance, cancellationToken); using (var channel = server.GetChannel(cancellationToken)) { var command = BsonDocument.Parse("{ ping : 1 }"); if (async) { channel .CommandAsync( session, ReadPreference.Primary, DatabaseNamespace.Admin, command, null, // payloads NoOpElementNameValidator.Instance, null, // additionalOptions null, // postWriteAction CommandResponseHandling.Return, BsonDocumentSerializer.Instance, new MessageEncoderSettings(), cancellationToken) .GetAwaiter() .GetResult(); } else { channel.Command( session, ReadPreference.Primary, DatabaseNamespace.Admin, command, null, // payloads NoOpElementNameValidator.Instance, null, // additionalOptions null, // postWriteAction CommandResponseHandling.Return, BsonDocumentSerializer.Instance, new MessageEncoderSettings(), cancellationToken); } } } var commandStartedEvent = eventCapturer.Next().Should().BeOfType <CommandStartedEvent>().Subject; commandStartedEvent.Command["apiVersion"].AsString.Should().Be("1"); }
public void Ensure_command_network_error_before_hadnshake_is_correctly_handled([Values(false, true)] bool async, [Values(false, true)] bool streamable) { var eventCapturer = new EventCapturer().Capture <ServerDescriptionChangedEvent>(); // ensure that hello or legacy hello check response is finished only after network error var hasNetworkErrorBeenTriggered = new TaskCompletionSource <bool>(); // ensure that there are no unexpected events between test ending and cluster disposing var hasClusterBeenDisposed = new TaskCompletionSource <bool>(); EndPoint initialSelectedEndpoint = null; using (var cluster = CreateAndSetupCluster(hasNetworkErrorBeenTriggered, hasClusterBeenDisposed, eventCapturer, streamable)) { ForceClusterId(cluster, __clusterId); // 0. Initial heartbeat via `connection.Open` // The next hello or legacy hello response will be delayed because the Task.WaitAny in the mock.Returns cluster.Initialize(); var selectedServer = cluster.SelectServer(CreateWritableServerAndEndPointSelector(__endPoint1), CancellationToken.None); initialSelectedEndpoint = selectedServer.EndPoint; initialSelectedEndpoint.Should().Be(__endPoint1); // make sure the next hello or legacy hello check has been called Thread.Sleep(__heartbeatInterval + TimeSpan.FromMilliseconds(50)); // 1. Trigger the command network error BEFORE handshake. At this time hello or legacy hello response is already delayed until `hasNetworkErrorBeenTriggered.SetResult` Exception exception; if (async) { exception = Record.Exception(() => selectedServer.GetChannelAsync(CancellationToken.None).GetAwaiter().GetResult()); } else { exception = Record.Exception(() => selectedServer.GetChannel(CancellationToken.None)); } var e = exception.Should().BeOfType <MongoConnectionException>().Subject; e.Message.Should().Be("DnsException"); // 2. Waiting for the hello or legacy hello check hasNetworkErrorBeenTriggered.SetResult(true); // unlock the in-progress hello or legacy hello response Thread.Sleep(100); // make sure the delayed hello or legacy hello check had time to change description if there is a bug var knownServers = cluster.Description.Servers.Where(s => s.Type != ServerType.Unknown); if (knownServers.Select(s => s.EndPoint).Contains(initialSelectedEndpoint)) { throw new Exception($"The type of failed server {initialSelectedEndpoint} has not been changed to Unknown."); } // ensure that a new server can be selected selectedServer = cluster.SelectServer(WritableServerSelector.Instance, CancellationToken.None); // ensure that the selected server is not the same as the initial selectedServer.EndPoint.Should().Be(__endPoint2); // the 4th event is MongoConnectionException which will trigger the next hello or legacy hello check immediately eventCapturer.WaitForOrThrowIfTimeout(events => events.Count() >= 4, TimeSpan.FromSeconds(5)); } hasClusterBeenDisposed.SetCanceled(); // Cut off not related events. Stop waiting in the latest mock.Returns for OpenAsync // Events asserting var initialHeartbeatEvents = new[] { // endpoints can be in random order eventCapturer.Next().Should().BeOfType <ServerDescriptionChangedEvent>().Subject, eventCapturer.Next().Should().BeOfType <ServerDescriptionChangedEvent>().Subject } .OrderBy(c => GetPort(c.NewDescription.EndPoint)) .ToList(); AssertEvent(initialHeartbeatEvents[0], __endPoint1, ServerType.ShardRouter, "Heartbeat"); AssertEvent(initialHeartbeatEvents[1], __endPoint2, ServerType.ShardRouter, "Heartbeat"); // the next 27018 events will be suppressed AssertNextEvent(eventCapturer, initialSelectedEndpoint, ServerType.Unknown, "InvalidatedBecause:ChannelException during handshake: MongoDB.Driver.MongoConnectionException: DnsException"); AssertNextEvent(eventCapturer, initialSelectedEndpoint, ServerType.Unknown, "Heartbeat", typeof(MongoConnectionException)); eventCapturer.Any().Should().BeFalse(); int GetPort(EndPoint endpoint) => ((DnsEndPoint)endpoint).Port; }
public void Dispose_should_raise_the_correct_events() { _subject.Dispose(); _capturedEvents.Next().Should().BeOfType <ConnectionClosingEvent>(); _capturedEvents.Next().Should().BeOfType <ConnectionClosedEvent>(); _capturedEvents.Any().Should().BeFalse(); }
public void Description_should_be_correct_after_initialization() { _settings = _settings.With(endPoints: new[] { _firstEndPoint }); var subject = CreateSubject(); subject.Initialize(); var description = subject.Description; description.State.Should().Be(ClusterState.Disconnected); description.Type.Should().Be(ClusterType.Unknown); description.Servers.Should().BeEquivalentTo(GetDescriptions(_firstEndPoint)); _capturedEvents.Next().Should().BeOfType <ClusterOpeningEvent>(); _capturedEvents.Next().Should().BeOfType <ClusterAddingServerEvent>(); _capturedEvents.Next().Should().BeOfType <ClusterAddedServerEvent>(); _capturedEvents.Next().Should().BeOfType <ClusterOpenedEvent>(); _capturedEvents.Next().Should().BeOfType <ClusterDescriptionChangedEvent>(); _capturedEvents.Any().Should().BeFalse(); }