예제 #1
0
        public async Task Misbehaved_async_method_should_not_deadlock_server_selection()
        {
            RequireServer.Check().ClusterType(ClusterType.ReplicaSet);

            // note: the code below deadlocks because await StartSessionAsync continues when UpdateClusterDescription in Cluster called TrySetResult after finding the secondary
            // but then the sync call to RunCommand blocks waiting for a primary and the call to TrySetResult never returns
            // which in turn prevents SDAM from unwinding back to process the next queued heartbeat event so the primary is never found

            var primary = CoreTestConfiguration.Cluster.Description.Servers.Where(s => s.Type == ServerType.ReplicaSetPrimary).Single();

            void clusterConfigurator(ClusterBuilder builder)
            {
                builder.Subscribe((ServerHeartbeatSucceededEvent heartbeatEvent) =>
                {
                    // ensure that the primary heartbeat is the last to be processed by introducing a small artificial delay
                    if (EndPointHelper.Equals(heartbeatEvent.ConnectionId.ServerId.EndPoint, primary.EndPoint))
                    {
                        Thread.Sleep(TimeSpan.FromSeconds(1));
                    }
                });
            }

            using (var client = DriverTestConfiguration.CreateDisposableClient(clusterConfigurator))
            {
                using (var session = await client.StartSessionAsync().ConfigureAwait(false))
                {
                    var adminDatabase = client.GetDatabase("admin");
                    adminDatabase.RunCommand <BsonDocument>(session, "{ ping : 1 }"); // this async method is misbehaving by calling a blocking sync method
                }
            }
        }
        public void Aggregate_out_to_collection_should_work()
        {
            var client        = DriverTestConfiguration.Client;
            var database      = client.GetDatabase("test");
            var collection    = database.GetCollection <BsonDocument>("test");
            var outCollection = database.GetCollection <AggregateCountResult>("out");

            var writeConcern = WriteConcern.WMajority;

            if (DriverTestConfiguration.IsReplicaSet(client))
            {
                var n = DriverTestConfiguration.GetReplicaSetNumberOfDataBearingMembers(client);
                writeConcern = new WriteConcern(n);
            }

            database.DropCollection("test");
            database.DropCollection("out");
            collection
            .WithWriteConcern(writeConcern)
            .InsertOne(new BsonDocument("_id", 1));

            var pipeline = new EmptyPipelineDefinition <BsonDocument>()
                           .Count()
                           .Out(outCollection)
                           .As <BsonDocument, AggregateCountResult, AggregateCountResultWithId>();
            var results = collection.WithReadPreference(ReadPreference.SecondaryPreferred).Aggregate(pipeline).ToList();

            results.Single().Count.Should().Be(1);
        }
예제 #3
0
 private DisposableMongoClient CreateDisposableClient(EventCapturer eventCapturer)
 {
     return(DriverTestConfiguration.CreateDisposableClient((MongoClientSettings settings) =>
     {
         settings.ClusterConfigurator = c => c.Subscribe(eventCapturer);
     }));
 }
예제 #4
0
        protected override MongoClient CreateClientForTestSetup()
        {
            var clientSettings = DriverTestConfiguration.GetClientSettings().Clone();

            clientSettings.GuidRepresentation = GuidRepresentation.Unspecified;
            return(new MongoClient(clientSettings));
        }
 private DisposableMongoClient CreateDisposableClient(BsonDocument test, EventCapturer eventCapturer)
 {
     return(DriverTestConfiguration.CreateDisposableClient((MongoClientSettings settings) =>
     {
         ConfigureClientSettings(settings, test);
         settings.ClusterConfigurator = c => c.Subscribe(eventCapturer);
     }));
 }
예제 #6
0
 private DisposableMongoClient CreateDisposableClient(EventCapturer eventCapturer)
 {
     return(DriverTestConfiguration.CreateDisposableClient((MongoClientSettings settings) =>
     {
         settings.HeartbeatInterval = TimeSpan.FromMilliseconds(5); // the default value for spec tests
         settings.ClusterConfigurator = c => c.Subscribe(eventCapturer);
     }));
 }
예제 #7
0
 private DisposableMongoClient CreateDisposableClient(EventCapturer eventCapturer)
 {
     return(DriverTestConfiguration.CreateDisposableClient((MongoClientSettings settings) =>
     {
         settings.ClusterConfigurator = c => c.Subscribe(eventCapturer);
         settings.RetryWrites = true;
     },
                                                           logger: LoggerFactory.CreateLogger <DisposableMongoClient>()));
 }
 private DisposableMongoClient CreateDisposableClient(EventCapturer eventCapturer)
 {
     return(DriverTestConfiguration.CreateDisposableClient((MongoClientSettings settings) =>
     {
         settings.HeartbeatInterval = TimeSpan.FromMilliseconds(5);
         settings.ClusterConfigurator = c => c.Subscribe(eventCapturer);
     },
                                                           logger: CreateLogger <DisposableMongoClient>()));
 }
        public void Driver_should_connect_to_AtlasDataLake_without_authentication()
        {
            RequireEnvironment.Check().EnvironmentVariable("ATLAS_DATA_LAKE_TESTS_ENABLED");

            using (var client = DriverTestConfiguration.CreateDisposableClient())
            {
                client.GetDatabase("admin").RunCommand <BsonDocument>(new BsonDocument("ping", 1));
            }
        }
        // private methods
        private DisposableMongoClient CreateClient(MongoClientSettings mongoClientSettings, EventCapturer eventCapturer, TimeSpan heartbeatInterval, string applicationName = null)
        {
            var clonedClientSettings = mongoClientSettings ?? DriverTestConfiguration.Client.Settings.Clone();

            clonedClientSettings.ApplicationName     = applicationName;
            clonedClientSettings.HeartbeatInterval   = heartbeatInterval;
            clonedClientSettings.ClusterConfigurator = builder => builder.Subscribe(eventCapturer);

            return(DriverTestConfiguration.CreateDisposableClient(clonedClientSettings));
        }
예제 #11
0
        private DisposableMongoClient CreateMongoClient(
            CollectionNamespace keyVaultNamespace = null,
            BsonDocument schemaMapDocument        = null,
            IReadOnlyDictionary <string, IReadOnlyDictionary <string, object> > kmsProviders = null,
            bool withExternalKeyVault = false,
            Action <ClusterBuilder> clusterConfigurator = null,
            Dictionary <string, object> extraOptions    = null,
            bool bypassAutoEncryption = false)
        {
            var mongoClientSettings = DriverTestConfiguration.GetClientSettings().Clone();

#pragma warning disable 618
            if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2)
            {
                mongoClientSettings.GuidRepresentation = GuidRepresentation.Unspecified;
            }
#pragma warning restore 618
            mongoClientSettings.ClusterConfigurator = clusterConfigurator;

            if (keyVaultNamespace != null || schemaMapDocument != null || kmsProviders != null || withExternalKeyVault)
            {
                if (extraOptions == null)
                {
                    extraOptions = new Dictionary <string, object>()
                    {
                        { "mongocryptdSpawnPath", Environment.GetEnvironmentVariable("MONGODB_BINARIES") ?? string.Empty }
                    };
                }

                var schemaMap = GetSchemaMapIfNotNull(schemaMapDocument);

                if (kmsProviders == null)
                {
                    kmsProviders = new ReadOnlyDictionary <string, IReadOnlyDictionary <string, object> >(new Dictionary <string, IReadOnlyDictionary <string, object> >());
                }

                var autoEncryptionOptions = new AutoEncryptionOptions(
                    keyVaultNamespace: keyVaultNamespace,
                    kmsProviders: kmsProviders,
                    schemaMap: schemaMap,
                    extraOptions: extraOptions,
                    bypassAutoEncryption: bypassAutoEncryption);

                if (withExternalKeyVault)
                {
                    var externalKeyVaultClientSettings = DriverTestConfiguration.GetClientSettings().Clone();
                    externalKeyVaultClientSettings.Credential = MongoCredential.FromComponents(null, null, "fake-user", "fake-pwd");
                    var externalKeyVaultClient = new MongoClient(externalKeyVaultClientSettings);
                    autoEncryptionOptions = autoEncryptionOptions.With(keyVaultClient: externalKeyVaultClient);
                }
                mongoClientSettings.AutoEncryptionOptions = autoEncryptionOptions;
            }

            return(new DisposableMongoClient(new MongoClient(mongoClientSettings)));
        }
예제 #12
0
 protected DisposableMongoClient CreateDisposableClient(BsonDocument test, EventCapturer eventCapturer)
 {
     return(DriverTestConfiguration.CreateDisposableClient(settings =>
     {
         ConfigureClientSettings(settings, test);
         if (eventCapturer != null)
         {
             settings.ClusterConfigurator = c => c.Subscribe(eventCapturer);
         }
     }));
 }
 private DisposableMongoClient CreateDisposableClient(BsonDocument test, EventCapturer eventCapturer, bool useMultipleShardRouters)
 {
     return(DriverTestConfiguration.CreateDisposableClient(
                (MongoClientSettings settings) =>
     {
         settings.HeartbeatInterval = TimeSpan.FromMilliseconds(5);     // the default value for spec tests
         ConfigureClientSettings(settings, test);
         settings.ClusterConfigurator = c => c.Subscribe(eventCapturer);
     },
                useMultipleShardRouters));
 }
        protected override MongoClient CreateClientForTestSetup()
        {
            var clientSettings = DriverTestConfiguration.GetClientSettings().Clone();

#pragma warning disable 618
            if (BsonDefaults.GuidRepresentationMode == GuidRepresentationMode.V2)
            {
                clientSettings.GuidRepresentation = GuidRepresentation.Unspecified;
            }
#pragma warning restore 618
            return(new MongoClient(clientSettings));
        }
        private DisposableMongoClient CreateDisposableClient(BsonDocument test)
        {
            var useMultipleShardRouters = test.GetValue("useMultipleMongoses", false).AsBoolean;
            var clientOptions           = (BsonDocument)test.GetValue("clientOptions", null);

            return(DriverTestConfiguration.CreateDisposableClient(
                       settings =>
            {
                ParseClientOptions(settings, clientOptions);
            },
                       useMultipleShardRouters));
        }
예제 #16
0
        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);
        }
 private DisposableMongoClient CreateDisposableClient(EventCapturer eventCapturer)
 {
     return(DriverTestConfiguration.CreateDisposableClient(
                (MongoClientSettings settings) =>
     {
         settings.ClusterConfigurator = c =>
         {
             c = CoreTestConfiguration.ConfigureCluster(c);
             c.Subscribe(eventCapturer);
             c.ConfigureServer(ss => ss.With(heartbeatInterval: Timeout.InfiniteTimeSpan));
         };
     }));
 }
        private DisposableMongoClient CreateDisposableClient(BsonDocument test, bool useMultipleShardRouters)
        {
            var clientOptions = (BsonDocument)test.GetValue("clientOptions", null);

            return(DriverTestConfiguration.CreateDisposableClient(
                       settings =>
            {
                settings.HeartbeatInterval = TimeSpan.FromMilliseconds(5);     // the default value for spec tests
                ParseClientOptions(settings, clientOptions);
            },
                       CreateLogger <DisposableMongoClient>(),
                       useMultipleShardRouters));
        }
예제 #19
0
 protected override async Task CallMethodAsync(CancellationToken cancellationToken)
 {
     using (var client = DriverTestConfiguration.CreateDisposableClient(_mongoClientSettings))
     {
         if (_session == null)
         {
             _result = await GetAdminDatabase(client).RunCommandAsync <BsonDocument>(_command, _readPreference, cancellationToken).ConfigureAwait(false);
         }
         else
         {
             _result = await GetAdminDatabase(client).RunCommandAsync <BsonDocument>(_session, _command, _readPreference, cancellationToken).ConfigureAwait(false);
         }
     }
 }
예제 #20
0
        protected DisposableMongoClient CreateDisposableClient(BsonDocument test, EventCapturer eventCapturer)
        {
            var useMultipleShardRouters = test.GetValue("useMultipleMongoses", false).AsBoolean;

            return(DriverTestConfiguration.CreateDisposableClient(
                       settings =>
            {
                ConfigureClientSettings(settings, test);
                if (eventCapturer != null)
                {
                    settings.ClusterConfigurator = c => c.Subscribe(eventCapturer);
                }
            },
                       useMultipleShardRouters));
        }
        protected DisposableMongoClient CreateDisposableClient(BsonDocument test, EventCapturer eventCapturer)
        {
            var useMultipleShardRouters = test.GetValue("useMultipleMongoses", false).AsBoolean;

            return(DriverTestConfiguration.CreateDisposableClient(
                       settings =>
            {
                settings.HeartbeatInterval = TimeSpan.FromMilliseconds(5);     // the default value for spec tests
                ConfigureClientSettings(settings, test);
                if (eventCapturer != null)
                {
                    settings.ClusterConfigurator = c => c.Subscribe(eventCapturer);
                }
            },
                       useMultipleShardRouters));
        }
예제 #22
0
        public void Aws_authentication_should_should_have_expected_result()
        {
            RequireEnvironment.Check().EnvironmentVariable("AWS_TESTS_ENABLED");

            using (var client = DriverTestConfiguration.CreateDisposableClient())
            {
                // test that a command that doesn't require auth completes normally
                var adminDatabase   = client.GetDatabase("admin");
                var isMasterCommand = new BsonDocument("ismaster", 1);
                var isMasterResult  = adminDatabase.RunCommand <BsonDocument>(isMasterCommand);

                // test that a command that does require auth completes normally
                var database   = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName);
                var collection = database.GetCollection <BsonDocument>(DriverTestConfiguration.CollectionNamespace.CollectionName);
                var count      = collection.CountDocuments(FilterDefinition <BsonDocument> .Empty);
            }
        }
        public void Monitor_sleep_at_least_minHeartbeatFreqencyMS_between_checks()
        {
            var minVersion = new SemanticVersion(4, 9, 0, "");

            RequireServer.Check().VersionGreaterThanOrEqualTo(minVersion);

            const string appName = "SDAMMinHeartbeatFrequencyTest";

            var failPointCommand = BsonDocument.Parse(
                $@"{{
                    configureFailPoint : 'failCommand',
                    mode : {{ 'times' : 5 }},
                    data :
                    {{
                        failCommands : [ 'isMaster' ],
                        errorCode : 1234,
                        appName : '{appName}'
                    }}
                }}");

            var settings      = DriverTestConfiguration.GetClientSettings();
            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

            settings.ApplicationName        = appName;
            settings.ServerSelectionTimeout = TimeSpan.FromSeconds(5);

            var server = DriverTestConfiguration.Client.Cluster.SelectServer(new EndPointServerSelector(new DnsEndPoint(serverAddress.Host, serverAddress.Port)), default);
            using var failPoint = FailPoint.Configure(server, NoCoreSession.NewHandle(), failPointCommand);
            using var client    = DriverTestConfiguration.CreateDisposableClient(settings);

            var database = client.GetDatabase(DriverTestConfiguration.DatabaseNamespace.DatabaseName);
            var sw       = Stopwatch.StartNew();
            _ = database.RunCommand <BsonDocument>("{ ping : 1 }");
            sw.Stop();

            sw.ElapsedMilliseconds.Should().BeInRange(2000, 3500);
        }
        public void Driver_should_connect_to_AtlasDataLake_with_SCRAM_SHA_256()
        {
            RequireEnvironment.Check().EnvironmentVariable("ATLAS_DATA_LAKE_TESTS_ENABLED");
            RequireServer.Check().Supports(Feature.ScramSha256Authentication);

            var connectionString = CoreTestConfiguration.ConnectionString;
            var username         = connectionString.Username;
            var password         = connectionString.Password;
            var source           = connectionString.AuthSource;

            var settings = DriverTestConfiguration.Client.Settings.Clone();

            settings.Credential = MongoCredential.FromComponents(mechanism: "SCRAM-SHA-256", source, username, password);

            using (var client = DriverTestConfiguration.CreateDisposableClient(settings))
            {
                client.GetDatabase("admin").RunCommand <BsonDocument>(new BsonDocument("ping", 1));
            }
        }
        public void KillCursors_should_return_expected_result()
        {
            RequireEnvironment.Check().EnvironmentVariable("ATLAS_DATA_LAKE_TESTS_ENABLED");
            RequireServer.Check();

            var databaseName   = "test";
            var collectionName = "driverdata";

            var eventCapturer = new EventCapturer()
                                .Capture <CommandStartedEvent>(x => "killCursors" == x.CommandName)
                                .Capture <CommandSucceededEvent>(x => new[] { "killCursors", "find" }.Contains(x.CommandName));

            using (var client = DriverTestConfiguration.CreateDisposableClient(eventCapturer))
            {
                var cursor = client
                             .GetDatabase(databaseName)
                             .GetCollection <BsonDocument>(collectionName)
                             .Find(new BsonDocument(), new FindOptions {
                    BatchSize = 2
                })
                             .ToCursor();

                var findCommandSucceededEvent = eventCapturer.Events.OfType <CommandSucceededEvent>().First(x => x.CommandName == "find");
                var findCommandResult         = findCommandSucceededEvent.Reply;
                var cursorId        = findCommandResult["cursor"]["id"].AsInt64;
                var cursorNamespace = CollectionNamespace.FromFullName(findCommandResult["cursor"]["ns"].AsString);

                cursor.Dispose();

                var killCursorsCommandStartedEvent   = eventCapturer.Events.OfType <CommandStartedEvent>().First(x => x.CommandName == "killCursors");
                var killCursorsCommandSucceededEvent = eventCapturer.Events.OfType <CommandSucceededEvent>().First(x => x.CommandName == "killCursors");
                var killCursorsStartedCommand        = killCursorsCommandStartedEvent.Command;

                cursorNamespace.DatabaseNamespace.DatabaseName.Should().Be(killCursorsCommandStartedEvent.DatabaseNamespace.DatabaseName);
                cursorNamespace.CollectionName.Should().Be(killCursorsStartedCommand["killCursors"].AsString);
                cursorId.Should().Be(killCursorsStartedCommand["cursors"][0].AsInt64);
                cursorId.Should().Be(killCursorsCommandSucceededEvent.Reply["cursorsKilled"][0].AsInt64);
            }
        }
        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);
        }
예제 #27
0
        private (DisposableMongoClient Client, Dictionary <string, EventCapturer> ClientEventCapturers) CreateClient(BsonDocument entity)
        {
            string appName = null;
            var    clientEventCapturers       = new Dictionary <string, EventCapturer>();
            string clientId                   = null;
            var    commandNamesToSkipInEvents = new List <string>();
            List <(string Key, IEnumerable <string> Events, List <string> CommandNotToCapture)> eventTypesToCapture = new ();
            bool?    loadBalanced            = null;
            int?     maxPoolSize             = null;
            var      readConcern             = ReadConcern.Default;
            var      retryReads              = true;
            var      retryWrites             = true;
            var      useMultipleShardRouters = false;
            TimeSpan?waitQueueTimeout        = null;
            var      writeConcern            = WriteConcern.Acknowledged;
            var      serverApi = CoreTestConfiguration.ServerApi;

            foreach (var element in entity)
            {
                switch (element.Name)
                {
                case "id":
                    clientId = element.Value.AsString;
                    break;

                case "uriOptions":
                    foreach (var option in element.Value.AsBsonDocument)
                    {
                        switch (option.Name)
                        {
                        case "appname":
                            appName = option.Value.ToString();
                            break;

                        case "loadBalanced":
                            loadBalanced = option.Value.ToBoolean();
                            break;

                        case "maxPoolSize":
                            maxPoolSize = option.Value.ToInt32();
                            break;

                        case "retryWrites":
                            retryWrites = option.Value.AsBoolean;
                            break;

                        case "retryReads":
                            retryReads = option.Value.AsBoolean;
                            break;

                        case "readConcernLevel":
                            var levelValue = option.Value.AsString;
                            var level      = (ReadConcernLevel)Enum.Parse(typeof(ReadConcernLevel), levelValue, true);
                            readConcern = new ReadConcern(level);
                            break;

                        case "w":
                            writeConcern = new WriteConcern(option.Value.AsInt32);
                            break;

                        case "waitQueueTimeoutMS":
                            waitQueueTimeout = TimeSpan.FromMilliseconds(option.Value.ToInt32());
                            break;

                        default:
                            throw new FormatException($"Invalid client uriOption argument name: '{option.Name}'.");
                        }
                    }
                    break;

                case "useMultipleMongoses":
                    useMultipleShardRouters = element.Value.AsBoolean;
                    break;

                case "observeEvents":
                    var observeEvents = element.Value.AsBsonArray.Select(x => x.AsString);
                    eventTypesToCapture.Add(
                        (Key: Ensure.IsNotNull(clientId, nameof(clientId)),
                         Events: observeEvents,
                         CommandNotToCapture: commandNamesToSkipInEvents));
                    break;

                case "ignoreCommandMonitoringEvents":
                    commandNamesToSkipInEvents.AddRange(element.Value.AsBsonArray.Select(x => x.AsString));
                    break;

                case "serverApi":
                    ServerApiVersion serverApiVersion           = null;
                    bool?            serverApiStrict            = null;
                    bool?            serverApiDeprecationErrors = null;
                    foreach (var option in element.Value.AsBsonDocument)
                    {
                        switch (option.Name)
                        {
                        case "version":
                            var serverApiVersionString = option.Value.AsString;
                            switch (serverApiVersionString)
                            {
                            case "1":
                                serverApiVersion = ServerApiVersion.V1;
                                break;

                            default:
                                throw new FormatException($"Invalid serverApi version: '{serverApiVersionString}'.");
                            }
                            break;

                        case "strict":
                            serverApiStrict = option.Value.AsBoolean;
                            break;

                        case "deprecationErrors":
                            serverApiDeprecationErrors = option.Value.AsBoolean;
                            break;

                        default:
                            throw new FormatException($"Invalid client serverApi argument name: '{option.Name}'.");
                        }
                    }
                    if (serverApiVersion != null)
                    {
                        serverApi = new ServerApi(serverApiVersion, serverApiStrict, serverApiDeprecationErrors);
                    }
                    break;

                case "storeEventsAsEntities":
                    var eventsBatches = element.Value.AsBsonArray;
                    foreach (var batch in eventsBatches.Cast <BsonDocument>())
                    {
                        var id     = batch["id"].AsString;
                        var events = batch["events"].AsBsonArray.Select(e => e.AsString);
                        eventTypesToCapture.Add((id, events, CommandNotToCapture: null));
                    }
                    break;

                default:
                    throw new FormatException($"Invalid client argument name: '{element.Name}'.");
                }
            }

            if (eventTypesToCapture.Count > 0)
            {
                var defaultCommandNamesToSkip = new List <string>
                {
                    "authenticate",
                    "buildInfo",
                    "configureFailPoint",
                    "getLastError",
                    "getnonce",
                    "hello",
                    OppressiveLanguageConstants.LegacyHelloCommandName,
                    "saslContinue",
                    "saslStart"
                };

                foreach (var eventsDetails in eventTypesToCapture)
                {
                    var commandNamesNotToCapture = Enumerable.Concat(eventsDetails.CommandNotToCapture ?? Enumerable.Empty <string>(), defaultCommandNamesToSkip);
                    var formatter     = _eventFormatters.ContainsKey(eventsDetails.Key) ? _eventFormatters[eventsDetails.Key] : null;
                    var eventCapturer = CreateEventCapturer(eventsDetails.Events, commandNamesNotToCapture, formatter);
                    clientEventCapturers.Add(eventsDetails.Key, eventCapturer);
                }
            }

            var eventCapturers = clientEventCapturers.Select(c => c.Value).ToArray();
            var client         = DriverTestConfiguration.CreateDisposableClient(
                settings =>
            {
                settings.ApplicationName       = appName;
                settings.LoadBalanced          = loadBalanced.GetValueOrDefault(defaultValue: settings.LoadBalanced);
                settings.MaxConnectionPoolSize = maxPoolSize.GetValueOrDefault(defaultValue: settings.MaxConnectionPoolSize);
                settings.RetryReads            = retryReads;
                settings.RetryWrites           = retryWrites;
                settings.ReadConcern           = readConcern;
                settings.WaitQueueTimeout      = waitQueueTimeout.GetValueOrDefault(defaultValue: settings.WaitQueueTimeout);
                settings.WriteConcern          = writeConcern;
                settings.HeartbeatInterval     = TimeSpan.FromMilliseconds(5); // the default value for spec tests
                settings.ServerApi             = serverApi;
                if (eventCapturers.Length > 0)
                {
                    settings.ClusterConfigurator = c =>
                    {
                        foreach (var eventCapturer in eventCapturers)
                        {
                            c.Subscribe(eventCapturer);
                        }
                    };
                }
            },
                useMultipleShardRouters);

            return(client, clientEventCapturers);
        }
        private void CreateClient(BsonDocument entity, out DisposableMongoClient client, out EventCapturer eventCapturer)
        {
            var eventTypesToCapture = new List <string>();
            var commandNamesToSkip  = new List <string>
            {
                "authenticate",
                "buildInfo",
                "configureFailPoint",
                "getLastError",
                "getnonce",
                "isMaster",
                "saslContinue",
                "saslStart"
            };

            var       readConcern             = ReadConcern.Default;
            var       retryReads              = true;
            var       retryWrites             = true;
            var       useMultipleShardRouters = false;
            var       writeConcern            = WriteConcern.Acknowledged;
            ServerApi serverApi = null;

            foreach (var element in entity)
            {
                switch (element.Name)
                {
                case "id":
                    // handled on higher level
                    break;

                case "uriOptions":
                    foreach (var option in element.Value.AsBsonDocument)
                    {
                        switch (option.Name)
                        {
                        case "retryWrites":
                            retryWrites = option.Value.AsBoolean;
                            break;

                        case "retryReads":
                            retryReads = option.Value.AsBoolean;
                            break;

                        case "readConcernLevel":
                            var levelValue = option.Value.AsString;
                            var level      = (ReadConcernLevel)Enum.Parse(typeof(ReadConcernLevel), levelValue, true);
                            readConcern = new ReadConcern(level);
                            break;

                        case "w":
                            writeConcern = new WriteConcern(option.Value.AsInt32);
                            break;

                        default:
                            throw new FormatException($"Unrecognized client uriOption name: '{option.Name}'.");
                        }
                    }
                    break;

                case "useMultipleMongoses":
                    useMultipleShardRouters = element.Value.AsBoolean;
                    break;

                case "observeEvents":
                    eventTypesToCapture.AddRange(element.Value.AsBsonArray.Select(x => x.AsString));
                    break;

                case "ignoreCommandMonitoringEvents":
                    commandNamesToSkip.AddRange(element.Value.AsBsonArray.Select(x => x.AsString));
                    break;

                case "serverApi":
                    ServerApiVersion serverApiVersion           = null;
                    bool?            serverApiStrict            = null;
                    bool?            serverApiDeprecationErrors = null;
                    foreach (var option in element.Value.AsBsonDocument)
                    {
                        switch (option.Name)
                        {
                        case "version":
                            var serverApiVersionString = option.Value.AsString;
                            switch (serverApiVersionString)
                            {
                            case "1":
                                serverApiVersion = ServerApiVersion.V1;
                                break;

                            default:
                                throw new FormatException($"Unrecognized serverApi version: '{serverApiVersionString}'.");
                            }
                            break;

                        case "strict":
                            serverApiStrict = option.Value.AsBoolean;
                            break;

                        case "deprecationErrors":
                            serverApiDeprecationErrors = option.Value.AsBoolean;
                            break;

                        default:
                            throw new FormatException($"Unrecognized client serverApi option name: '{option.Name}'.");
                        }
                    }
                    if (serverApiVersion != null)
                    {
                        serverApi = new ServerApi(serverApiVersion, serverApiStrict, serverApiDeprecationErrors);
                    }
                    break;

                default:
                    throw new FormatException($"Unrecognized client entity field: '{element.Name}'.");
                }
            }

            eventCapturer = null;
            if (eventTypesToCapture.Count > 0)
            {
                eventCapturer = new EventCapturer();
                foreach (var eventTypeToCapture in eventTypesToCapture)
                {
                    switch (eventTypeToCapture)
                    {
                    case "commandStartedEvent":
                        eventCapturer = eventCapturer.Capture <CommandStartedEvent>(x => !commandNamesToSkip.Contains(x.CommandName));
                        break;

                    case "commandSucceededEvent":
                        eventCapturer = eventCapturer.Capture <CommandSucceededEvent>(x => !commandNamesToSkip.Contains(x.CommandName));
                        break;

                    case "commandFailedEvent":
                        eventCapturer = eventCapturer.Capture <CommandFailedEvent>(x => !commandNamesToSkip.Contains(x.CommandName));
                        break;

                    default:
                        throw new FormatException($"Invalid event name: {eventTypeToCapture}.");
                    }
                }
            }

            var localEventCapturer = eventCapturer; // copy value of eventCapturer ref variable to a local variable (to avoid error CS1628)

            client = DriverTestConfiguration.CreateDisposableClient(
                settings =>
            {
                settings.RetryReads        = retryReads;
                settings.RetryWrites       = retryWrites;
                settings.ReadConcern       = readConcern;
                settings.WriteConcern      = writeConcern;
                settings.HeartbeatInterval = TimeSpan.FromMilliseconds(5);     // the default value for spec tests
                if (localEventCapturer != null)
                {
                    settings.ClusterConfigurator = c => c.Subscribe(localEventCapturer);
                }
                settings.ServerApi = serverApi;
            },
                useMultipleShardRouters);
        }
        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 methods
        private DisposableMongoClient CreateDisposableClient()
        {
            var mongoClientSettings = MongoClientSettings.FromConnectionString("mongodb://hostnotneeded");

            return(DriverTestConfiguration.CreateDisposableClient(mongoClientSettings));
        }