CanConnectAsync does not correctly dispose connection when pooling is active
See original GitHub issueCanConnectasync is called from health checks regularly inside container in a kubernetes cluster, but its use can reach max connections for client.
The problem is reproducible in localhost and in my staging environment.
I’ve created a test console app (.net 5.0) to reproduce the issue:
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace NpgsqlTests
{
static class Program
{
static async Task Main(string[] args) {
var pooling = true;
var services = new ServiceCollection();
services.AddDbContext<TestDbContext>(o => {
o.UseNpgsql($"Host=localhost;Database=npgsqltests;Username=postgres;Password=postgres;Pooling={pooling}");
});
using var provider = services.BuildServiceProvider();
await CreateDatabase(provider);
await TestQueries(provider);
await TestCanConnect(provider);
}
private static async Task TestCanConnect(ServiceProvider provider) {
Console.WriteLine("Start connections.");
for (int i = 0; i < 200; i++) {
await Task.Delay(100);
Console.Write($"Connection {i}...");
var scope = provider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<TestDbContext>();
await dbContext.Database.CanConnectAsync();
Console.WriteLine(" Done.");
}
}
private static async Task TestQueries(ServiceProvider provider) {
Console.WriteLine("Start queries.");
for (int i = 0; i < 200; i++) {
await Task.Delay(100);
Console.Write($"Query {i}...");
var scope = provider.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<TestDbContext>();
dbContext.Entities.Add(new TestEntity());
await dbContext.SaveChangesAsync();
var c = await dbContext.Entities.CountAsync();
Console.WriteLine(" Done.");
}
}
private static async Task CreateDatabase(ServiceProvider provider) {
using (var scope = provider.CreateScope()) {
var dbContext = scope.ServiceProvider.GetRequiredService<TestDbContext>();
await dbContext.Database.EnsureCreatedAsync();
}
}
}
public class TestDbContext : DbContext
{
public DbSet<TestEntity> Entities { get; set; }
public TestDbContext(DbContextOptions<TestDbContext> options) : base(options) {
}
}
public class TestEntity
{
public int Id { get; set; }
public string Text { get; set; }
}
}
The project has two dependencies:
<ItemGroup>
<PackageReference Include="Npgsql" Version="5.0.4" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.5" />
</ItemGroup>
When var pooling = true
, the method TestCanConnect
stucks after 100 calls of CanConnectAsync
.
I see all the connection in idle state when querying pg_stat_activity
.
When var pooling = false
, everything runs without problems, and pg_stat_activity
is almost empty.
The method TestQueries
is used to ensure that usual queries are executed without problems; in fact, it runs successfully regardless of the value of pooling
.
I think the problem is in disposal of the connection when connection pooling is temporarily disabled inside CanConnectAsync
.
Issue Analytics
- State:
- Created 2 years ago
- Comments:10 (5 by maintainers)
Thanks for your feedback, and sorry this bug occurred in the first place…
@roji I’ve tested 5.0.5.1 in my staging environment, using original health check for ef core, and everything goes well, no more connection exhaustion.
Thank you very much!