C# MongoDB客户端引发等待队列已满异常

C# MongoDB客户端引发等待队列已满异常,c#,mongodb,multithreading,C#,Mongodb,Multithreading,我看到了一个奇怪的问题,MongoDB的.NET客户端抛出了一个等待队列,等待获取到服务器127.0.0.1:27017的连接,队列已满。异常 我有一个大小为10的信号灯来保护对MongoDB的任何调用。 这意味着,对Mongo的并发调用永远不会超过10个 .NET驱动程序的默认连接池大小为100,大于10。 因此,10个并发调用不应该是一个问题 为了复制这一点,我有下面的代码,是的,但它使问题变得明显 我还为MongoDB找到了这个规范 这有关系吗? 是否每个调用线程(在本例中为线程池工作线

我看到了一个奇怪的问题,MongoDB的.NET客户端抛出了一个
等待队列,等待获取到服务器127.0.0.1:27017的连接,队列已满。
异常

我有一个大小为10的信号灯来保护对MongoDB的任何调用。 这意味着,对Mongo的并发调用永远不会超过10个

.NET驱动程序的默认连接池大小为100,大于10。 因此,10个并发调用不应该是一个问题

为了复制这一点,我有下面的代码,是的,但它使问题变得明显

我还为MongoDB找到了这个规范

这有关系吗? 是否每个调用线程(在本例中为线程池工作线程)都进入等待队列并尝试获取连接,如果我有更多的工作线程,即使并发级别较低,也必须将连接分配给这个新的调用工作线程

using System;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using System.Threading;

namespace ConsoleApp58
{
    public class AsyncSemaphore
    {
        private readonly SemaphoreSlim _semaphore;

        public AsyncSemaphore(int maxConcurrency)
        {
            _semaphore = new SemaphoreSlim(
                maxConcurrency,
                maxConcurrency
            );
        }

        public async Task<T> WaitAsync<T>(Task<T> task)
        {
            await _semaphore.WaitAsync();
            //proves we have the correct max concurrent calls
            //  Console.WriteLine(_semaphore.CurrentCount);

            try
            {
                var result = await task;
                return result;
            }
            finally
            {
                _semaphore.Release();
            }
        }
    }
    
    class Program
    {
        public class SomeEntity
        {
            public ObjectId Id { get; set; }
            public string Name { get; set; }
        }

        static void Main(string[] args)
        {
            var settings = MongoClientSettings.FromUrl(MongoUrl.Create("mongodb://127.0.0.1:27017"));
            // settings.MinConnectionPoolSize = 10;
            // settings.MaxConnectionPoolSize = 1000;
            
            // I get that I can tweak settings, but I want to know why this occurs at all?
            // if we guard the calls with a semaphore, how can this happen?

            var mongoClient = new MongoClient(settings);
            var someCollection = mongoClient.GetDatabase("dummydb").GetCollection<SomeEntity>("some");
            var a = new AsyncSemaphore(10);
            
            // is this somehow related ?
            // https://github.com/mongodb/specifications/blob/master/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst#id94


            _ = Task.Run(() =>
                {
                    while (true)
                    {
                        // this bit is protected by a semaphore of size 10
                        // (we will flood the thread pool with ongoing tasks, yes)
                        _ = a.WaitAsync(RunTask(someCollection))
                            //after the task is done, dump the result
                            // dot is OK, else exception message
                            .ContinueWith(x =>
                            {
                                if (x.IsFaulted)
                                {
                                    Console.WriteLine(x.Exception);
                                }
                            });
                    }
                }
            );

            Console.ReadLine();
        }

        private static async Task<SomeEntity> RunTask(IMongoCollection<SomeEntity> pids)
        {
            //simulate some mongo interaction here
            var res = await pids.Find(x => x.Name == "").FirstOrDefaultAsync();
            return res;
        }
    }
}
使用系统;
使用System.Threading.Tasks;
使用MongoDB.Bson;
使用MongoDB.Driver;
使用系统线程;
名称空间控制台EAPP58
{
公共类异步信号量
{
私有只读信号量limu信号量;
公共异步信号量(int-maxConcurrency)
{
_信号量=新信号量lim(
maxConcurrency,
最大并发
);
}
公共异步任务WaitAsync(任务任务)
{
wait_信号量。WaitAsync();
//证明我们有正确的最大并发调用数
//Console.WriteLine(_semaphore.CurrentCount);
尝试
{
var结果=等待任务;
返回结果;
}
最后
{
_semaphore.Release();
}
}
}
班级计划
{
公共类实体
{
公共对象Id{get;set;}
公共字符串名称{get;set;}
}
静态void Main(字符串[]参数)
{
var settings=MongoClientSettings.FromUrl(MongoUrl.Create(“mongodb://127.0.0.1:27017"));
//settings.MinConnectionPoolSize=10;
//settings.MaxConnectionPoolSize=1000;
//我知道我可以调整设置,但我想知道为什么会发生这种情况?
//如果我们用信号量来保护呼叫,怎么会发生这种情况?
var mongoClient=新的mongoClient(设置);
var someCollection=mongoClient.GetDatabase(“dummydb”).GetCollection(“some”);
var a=新的异步信号量(10);
//这有什么关系吗?
// https://github.com/mongodb/specifications/blob/master/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst#id94
_=任务。运行(()=>
{
while(true)
{
//此位受大小为10的信号量保护
//(我们将用正在进行的任务淹没线程池,是的)
_=a.WaitAsync(RunTask(someCollection))
//任务完成后,转储结果
//dot正常,否则显示异常消息
.ContinueWith(x=>
{
如果(x.IsFaulted)
{
Console.WriteLine(x.Exception);
}
});
}
}
);
Console.ReadLine();
}
专用静态异步任务RunTask(IMongoCollection pids)
{
//在这里模拟一些mongo交互
var res=await pids.Find(x=>x.Name==“”)。FirstOrDefaultAsync();
返回res;
}
}
}

建立连接需要时间。您不会立即获得100个可用连接。如果您创建一个客户机并立即请求10个操作,而没有可用的连接,则可以点击等待队列超时

一些驱动程序也有等待队列长度限制。它不是标准化的,我认为应该弃用它,但出于兼容性的原因,它可能会继续存在。请查阅驾驶员文档,了解如何提升

然后,增加waitQueueTimeoutMS或逐渐增加负载,或者在启动负载之前等待建立连接(后者可以使用CMAP事件)

确保由10个未完成操作组成的并发边界也能正常工作