question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Connection Pool does not free idle connections in version 4.0

See original GitHub issue

Hello,

It seems like the connection pool has changed in a way that seems undesired. In version 3, only as many connections as are needed are kept open, while in version 4, once a connection is opened the connection is kept alive indefinitely in normal circumstances.

For example, if you open 10 connections with “Connection Idle Lifetime=15” and then, in sequence, do “new NpgsqlConnection(blah).Open” every 100 ms, you will never drop down to 1 connection again (version 3 does drop down to 1 connection).

Since the parameter “Connection Idle Lifetime” exists and is now effectively useless as long as there is any load, I think that this a bug. Version 3 implemented the “Next idle connection” as a stack (List of connections with “removefirst” and “addFirst”). While version 4 takes a random idle connection. Where random is Thread.CurrentThread.ManagedThreadId % _max, effectively extending the a random connection by 15 seconds.

Code to replicate below, please not that since the ManagedThreadId is used as the random number generator, how you execute it might prevent the issue from being replicated. Works fine for me though and I hope the issue is clear enough 😃

static void Main(string[] args)
        {
            var tasks = new List<Task>();
            for(int i = 0; i < 10; i++)
            {
                tasks.Add(Task.Run(() =>
                    {
                        using (var connection = new NpgsqlConnection("{connectionStringHere};Connection Idle Lifetime=15"))
                        {
                            connection.Open();

                            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                            Thread.Sleep(1000);
                        }
                    }
                ));
            }
            
            Task.WaitAll(tasks.ToArray());
            Console.WriteLine("Created 10 connections");
            while (true)
            {
                //new Task is used in order to create a new ManagedTradeId which is used as a random number in ConnectionPool.cs. In IIS requests will generally have quite a spread of thread ids.
                var task = Task.Run(() =>
                {
                    using (var connection = new NpgsqlConnection("{ConnectionStringHere};Connection Idle Lifetime=15"))
                    {
                        connection.Open();
                        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                        Thread.Sleep(100);
                    }
                });
                task.Wait();
            }
            
        }

Issue observed on: NPGSQL 4.0.3.0 (latest stable nuget package) Windows Server 2012 .NET 4.5.2

Cheers,

Tom

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:3
  • Comments:17 (10 by maintainers)

github_iconTop GitHub Comments

3reactions
rojicommented, Nov 14, 2018

OK, thanks for the feedback. As this is a bug in the current behavior, and the fix is relatively risk-free (despite touching the pool…) I’ll try to do it for the next patch release. I’ll try to see about benchmarking the effects although I don’t have a good platform for that.

3reactions
rojicommented, Nov 12, 2018

The description above is correct… The pool radically changed in version 4.0, rewritten to be completely lock-free to increase performance. Unfortunately this meant that we abandoned the previous stack design, which ensured we always return the most recently used connections, and therefore allow the least recently used connections to be pruned after idle time.

The idea of using the managed thread ID modulu _max was to start searching in random places in the array, so as to avoid “contention” at the start of the idle list, where many threads attempt to perform interlocked operations on the very same slot of the array.

We can indeed recreate the previous logic by having Allocate() always start at index 0 and look for a connector moving forward, while having Release() start at the last slot and moving backwards. This would ensure that open attempts (usually!) return the last connection released back into the pool. The downside is a potential small slowdown as attempts contend on the same slots (open attempts always at the start, release attempts always at the end). I think I did a bit of benchmarking at some point and the random starting point wasn’t actually that important, so this should be OK - but we need to check again. Or we could think about doing something more fancy.

@YohDeadfall @austindrenski any thoughts?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Connection pool (Version 4) settings (deprecated)
A connection is not idled if removing the connection would cause the pool to shrink below its minimum size. Setting this value to...
Read more >
Tomcat connection pool not releasing idle connections
No. The idle connector will just close idle connections to get down to minIdle. It won't close a connection just to reopen another...
Read more >
JDBC Connection Pool Settings
For best performance, set Idle Timeout to zero (0) seconds, so that idle connections will not be removed. This ensures that there is...
Read more >
What is connection pooling, and why should you care
Database connection pooling is a way to reduce the cost of opening and closing connections by maintaining a “pool” of open connections that...
Read more >
SQL Server Connection Pooling - ADO.NET
The connection pooler removes a connection from the pool after it has been idle for approximately 4-8 minutes, or if the pooler detects...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found