public async Task TestWorksWithAmbientTransaction() { using var connection = new NpgsqlConnection(TestingPostgresDb.ConnectionString); await connection.OpenAsync(); var connectionLock = new PostgresDistributedLock(new PostgresAdvisoryLockKey("AmbTrans"), connection); var otherLock = new PostgresDistributedLock(connectionLock.Key, TestingPostgresDb.ConnectionString); using var otherLockHandle = await otherLock.AcquireAsync(); using (var transaction = connection.BeginTransaction()) { using var transactionCommand = connection.CreateCommand(); transactionCommand.Transaction = transaction; transactionCommand.CommandText = "SET LOCAL statement_timeout = 1010"; await transactionCommand.ExecuteNonQueryAsync(); using (var timedOutHandle = await connectionLock.TryAcquireAsync(TimeSpan.FromSeconds(.2))) { Assert.IsNull(timedOutHandle); } (await GetTimeoutAsync(transactionCommand)).ShouldEqual("1010ms"); var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(.3)); var task = connectionLock.AcquireAsync(cancellationToken: cancellationTokenSource.Token).AsTask(); task.ContinueWith(_ => { }).Wait(TimeSpan.FromSeconds(5)).ShouldEqual(true); task.Status.ShouldEqual(TaskStatus.Canceled); (await GetTimeoutAsync(transactionCommand)).ShouldEqual("1010ms"); } using var connectionCommand = connection.CreateCommand(); (await GetTimeoutAsync(connectionCommand)).ShouldEqual("0");
public async Task TestInt64AndInt32PairKeyNamespacesAreDifferent() { var connectionString = TestingPostgresDb.ConnectionString; var key1 = new PostgresAdvisoryLockKey(0); var key2 = new PostgresAdvisoryLockKey(0, 0); var @lock1 = new PostgresDistributedLock(key1, connectionString); var @lock2 = new PostgresDistributedLock(key2, connectionString); using var handle1 = await lock1.TryAcquireAsync(); Assert.IsNotNull(handle1); using var handle2 = await lock2.TryAcquireAsync(); Assert.IsNotNull(handle2); }
public static int Main(string[] args) { var type = args[0]; var name = args[1]; IDisposable?handle; switch (type) { case nameof(SqlDistributedLock): handle = new SqlDistributedLock(name, SqlServerCredentials.ConnectionString).Acquire(); break; case "Write" + nameof(SqlDistributedReaderWriterLock): handle = new SqlDistributedReaderWriterLock(name, SqlServerCredentials.ConnectionString).AcquireWriteLock(); break; case nameof(SqlDistributedSemaphore) + "1AsMutex": handle = new SqlDistributedSemaphore(name, maxCount: 1, connectionString: SqlServerCredentials.ConnectionString).Acquire(); break; case nameof(SqlDistributedSemaphore) + "5AsMutex": handle = new SqlDistributedSemaphore(name, maxCount: 5, connectionString: SqlServerCredentials.ConnectionString).Acquire(); break; case nameof(PostgresDistributedLock): handle = new PostgresDistributedLock(new PostgresAdvisoryLockKey(name), PostgresCredentials.GetConnectionString(Environment.CurrentDirectory)).Acquire(); break; case "Write" + nameof(PostgresDistributedReaderWriterLock): handle = new PostgresDistributedReaderWriterLock(new PostgresAdvisoryLockKey(name), PostgresCredentials.GetConnectionString(Environment.CurrentDirectory)).AcquireWriteLock(); break; case nameof(EventWaitHandleDistributedLock): handle = new EventWaitHandleDistributedLock(name).Acquire(); break; case nameof(WaitHandleDistributedSemaphore) + "1AsMutex": handle = new WaitHandleDistributedSemaphore(name, maxCount: 1).Acquire(); break; case nameof(WaitHandleDistributedSemaphore) + "5AsMutex": handle = new WaitHandleDistributedSemaphore(name, maxCount: 5).Acquire(); break; case nameof(AzureBlobLeaseDistributedLock): handle = new AzureBlobLeaseDistributedLock( new BlobClient(AzureCredentials.ConnectionString, AzureCredentials.DefaultBlobContainerName, name), o => o.Duration(TimeSpan.FromSeconds(15)) ) .Acquire(); break; case nameof(FileDistributedLock): handle = new FileDistributedLock(new FileInfo(name)).Acquire(); break; case nameof(RedisDistributedLock) + "1": handle = AcquireRedisLock(name, serverCount: 1); break; case nameof(RedisDistributedLock) + "3": handle = AcquireRedisLock(name, serverCount: 3); break; case nameof(RedisDistributedLock) + "2x1": handle = AcquireRedisLock(name, serverCount: 2); // we know the last will fail; don't bother (we also don't know its port) break; case nameof(RedisDistributedLock) + "1WithPrefix": handle = AcquireRedisLock("distributed_locks:" + name, serverCount: 1); break; case "Write" + nameof(RedisDistributedReaderWriterLock) + "1": handle = AcquireRedisWriteLock(name, serverCount: 1); break; case "Write" + nameof(RedisDistributedReaderWriterLock) + "3": handle = AcquireRedisWriteLock(name, serverCount: 3); break; case "Write" + nameof(RedisDistributedReaderWriterLock) + "2x1": handle = AcquireRedisWriteLock(name, serverCount: 2); // we know the last will fail; don't bother (we also don't know its port) break; case "Write" + nameof(RedisDistributedReaderWriterLock) + "1WithPrefix": handle = AcquireRedisWriteLock("distributed_locks:" + name, serverCount: 1); break; case string _ when type.StartsWith(nameof(RedisDistributedSemaphore)): { var maxCount = type.EndsWith("1AsMutex") ? 1 : type.EndsWith("5AsMutex") ? 5 : throw new ArgumentException(type); handle = new RedisDistributedSemaphore( name, maxCount, GetRedisDatabases(serverCount: 1).Single(), // in order to see abandonment work in a reasonable timeframe, use very short expiry options => options.Expiry(TimeSpan.FromSeconds(1)) .BusyWaitSleepTime(TimeSpan.FromSeconds(.1), TimeSpan.FromSeconds(.3)) ).Acquire(); break; } default: Console.Error.WriteLine($"type: {type}"); return(123); } Console.WriteLine("Acquired"); Console.Out.Flush(); if (Console.ReadLine() != "abandon") { handle.Dispose(); } return(0); }