madelson / DistributedLock

A .NET library for distributed synchronization
MIT License
1.86k stars 192 forks source link

Error acquiring lock on PostgreSQL when using Npgsql 8.0 #174

Closed wazzamatazz closed 10 months ago

wazzamatazz commented 10 months ago

I'm using DistributedLock.Postgres in a project that is using EF Core 7 and Npgsql 7. If I update the project to use EF Core 8 and Npgsql 8 RC2 I am no longer able to acquire a lock. Instead, I see an error and stack trace like the following:

System.InvalidOperationException: Unexpected value '' from acquire command
   at Medallion.Threading.Postgres.PostgresAdvisoryLock.TryAcquireAsync(DatabaseConnection connection, String resourceName, TimeoutValue timeout, CancellationToken cancellationToken) in /_/DistributedLock.Postgres/PostgresAdvisoryLock.cs:line 112
   at Medallion.Threading.Internal.Data.MultiplexedConnectionLock.TryAcquireAsync[TLockCookie](String name, TimeoutValue timeout, IDbSynchronizationStrategy`1 strategy, TimeoutValue keepaliveCadence, CancellationToken cancellationToken, Boolean opportunistic) in /_/DistributedLock.Core/Internal/Data/MultiplexedConnectionLock.cs:line 74
   at Medallion.Threading.Internal.Data.MultiplexedConnectionLock.TryAcquireAsync[TLockCookie](String name, TimeoutValue timeout, IDbSynchronizationStrategy`1 strategy, TimeoutValue keepaliveCadence, CancellationToken cancellationToken, Boolean opportunistic) in /_/DistributedLock.Core/Internal/Data/MultiplexedConnectionLock.cs:line 94
   at Medallion.Threading.Internal.Data.MultiplexedConnectionLockPool.TryAcquireAsync[TLockCookie](String connectionString, String name, TimeoutValue timeout, IDbSynchronizationStrategy`1 strategy, TimeoutValue keepaliveCadence, CancellationToken cancellationToken) in /_/DistributedLock.Core/Internal/Data/MultiplexedConnectionLockPool.cs:line 91
   at Medallion.Threading.Internal.Data.MultiplexedConnectionLockPool.TryAcquireAsync[TLockCookie](String connectionString, String name, TimeoutValue timeout, IDbSynchronizationStrategy`1 strategy, TimeoutValue keepaliveCadence, CancellationToken cancellationToken) in /_/DistributedLock.Core/Internal/Data/MultiplexedConnectionLockPool.cs:line 97
   at Medallion.Threading.Internal.DistributedLockHelpers.Wrap[THandle](ValueTask`1 handleTask, Func`2 factory) in /_/DistributedLock.Core/Internal/DistributedLockHelpers.cs:line 41
   at Medallion.Threading.Internal.DistributedLockHelpers.ThrowTimeoutIfNull[T](ValueTask`1 task, String object) in /_/DistributedLock.Core/Internal/DistributedLockHelpers.cs:line 142

I am creating the lock by passing the connection string to the PostgresDistributedLock constructor rather than an IDbConnection instance but I've found that I get the same error regardless of which constructor I use.

Reverting to EF Core 7/Npgsql 7 immediately fixes the issue.

Kaffeetasse commented 10 months ago

I think the reason is, the AcquireCommand returns now a null value instead of DBNull.

In this line: https://github.com/madelson/DistributedLock/blob/00c0f4e926c586f5a1fabf869b79817a1cefbe46/src/DistributedLock.Postgres/PostgresAdvisoryLock.cs#L66

valentasm1 commented 10 months ago

Any workaround?

madelson commented 10 months ago

@Kaffeetasse appreciate the diagnosis. Seems like this is a breaking change in Npgsql (I wonder if it is intentional).

fixing this in the package would be very easy; we can simply detect when it gets the “wrong” value and convert to what we expect.

I’d be happy to look at a PR for this. Otherwise, I can hopefully get to it in a few weeks.

valentasm1 commented 10 months ago

Any time when someone will find time to review and release it?

madelson commented 10 months ago

@valentasm1 I should be able to do it in the next few days.