-
Notifications
You must be signed in to change notification settings - Fork 241
Open
Description
Hi,
Thank you for this great library.
We are encountering a System.InvalidOperationException: Connection is not open error in our production environment.
We see this error about 100-200 times per day under load.
Environment:
- Library Version: latest
- Npgsql Version: 9.0.4 and 10.0.1
- Framework: .NET 9 and .NET 10
- Database: PostgreSQL 18
Reproduction:
I tried to reproduce the issue locally using a PostgreSQL Docker container (with increased connection limit: 999) and the following console application. The issue seems to appear under high concurrency.
using Medallion.Threading;
using Medallion.Threading.Postgres;
var list = Enumerable.Range(1, 200).ToList();
var tasks = new List<Task>();
var error = new List<Exception>();
IDistributedLockProvider synchronizationProvider =
new PostgresDistributedSynchronizationProvider(
"User ID=xxxx;Password=xxxx;Database=xxxx;Host=127.0.0.1;Port=5432;Pooling=true");
for (var j = 0; j < 30; j++)
{
foreach (var id in list)
{
var x = Task.Run(
async () =>
{
try
{
var timeout = TimeSpan.FromMinutes(5);
var key = "X" + id;
await using var writeLock =
await synchronizationProvider.CreateLock(key)
.AcquireAsync(timeout);
await Task.Delay(
Random.Shared.Next(1, 10),
writeLock.HandleLostToken);
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.WriteLine(ex.StackTrace);
Environment.Exit(1);
}
});
tasks.Add(x);
}
await Task.WhenAll(tasks);
}
Console.WriteLine("Task completed");
Stack Trace:
System.InvalidOperationException: Connection is not open
at Npgsql.ThrowHelper.ThrowInvalidOperationException(String message)
at Npgsql.NpgsqlCommand.CheckAndGetConnection()
at Npgsql.NpgsqlCommand.Prepare(Boolean async, CancellationToken cancellationToken)
at Npgsql.NpgsqlCommand.PrepareAsync(CancellationToken cancellationToken)
at Medallion.Threading.Internal.Data.DatabaseCommand.PrepareIfNeededAsync(CancellationToken cancellationToken) in /_/src/DistributedLock.Core/Internal/Data/DatabaseCommand.cs:line 182
at Medallion.Threading.Internal.Data.DatabaseCommand.ExecuteAsync[TResult](Func`3 executeAsync, Func`2 executeSync, CancellationToken cancellationToken, Boolean disallowAsyncCancellation, Boolean isConnectionMonitoringQuery) in /_/src/DistributedLock.Core/Internal/Data/DatabaseCommand.cs:line 81
at Medallion.Threading.Postgres.PostgresAdvisoryLock.ReleaseAsync(DatabaseConnection connection, PostgresAdvisoryLockKey key, Boolean isTry) in /_/src/DistributedLock.Postgres/PostgresAdvisoryLock.cs:line 278
at Medallion.Threading.Internal.Data.MultiplexedConnectionLock.ReleaseAsync[TLockCookie](IDbSynchronizationStrategy`1 strategy, String name, TLockCookie lockCookie) in /_/src/DistributedLock.Core/Internal/Data/MultiplexedConnectionLock.cs:line 154
at Medallion.Threading.Internal.Data.MultiplexedConnectionLock.ReleaseAsync[TLockCookie](IDbSynchronizationStrategy`1 strategy, String name, TLockCookie lockCookie) in /_/src/DistributedLock.Core/Internal/Data/MultiplexedConnectionLock.cs:line 168
....
It looks like the connection might be closed or returned to the pool unexpectedly while the lock is trying to release or prepare a command.
Do you have any idea how to fix this or what might be causing the race condition?
Thanks
madelson