-
Notifications
You must be signed in to change notification settings - Fork 196
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #69 from madelson/release-2.0.1
Release 2.0.1
- Loading branch information
Showing
12 changed files
with
379 additions
and
308 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
329 changes: 179 additions & 150 deletions
329
DistributedLock.Tests/AbstractTestCases/Redis/RedisSynchronizationCoreTestCases.cs
Large diffs are not rendered by default.
Oops, something went wrong.
132 changes: 71 additions & 61 deletions
132
DistributedLock.Tests/Infrastructure/Redis/TestingRedisDatabaseProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,71 @@ | ||
using Medallion.Threading.Tests.Redis; | ||
using NUnit.Framework; | ||
using StackExchange.Redis; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace Medallion.Threading.Tests.Redis | ||
{ | ||
public abstract class TestingRedisDatabaseProvider | ||
{ | ||
protected TestingRedisDatabaseProvider(IEnumerable<IDatabase> databases) | ||
{ | ||
this.Databases = databases.ToArray(); | ||
} | ||
|
||
protected TestingRedisDatabaseProvider(int count) | ||
: this(Enumerable.Range(0, count).Select(i => RedisServer.GetDefaultServer(i).Multiplexer.GetDatabase())) | ||
{ | ||
} | ||
|
||
// publicly settable so that callers can alter the dbs in use | ||
public IReadOnlyList<IDatabase> Databases { get; set; } | ||
|
||
public virtual string CrossProcessLockTypeSuffix => this.Databases.Count.ToString(); | ||
} | ||
|
||
public sealed class TestingRedisSingleDatabaseProvider : TestingRedisDatabaseProvider | ||
{ | ||
public TestingRedisSingleDatabaseProvider() : base(count: 1) { } | ||
} | ||
|
||
public sealed class TestingRedis3DatabaseProvider : TestingRedisDatabaseProvider | ||
{ | ||
public TestingRedis3DatabaseProvider() : base(count: 3) { } | ||
} | ||
|
||
public sealed class TestingRedis2x1DatabaseProvider : TestingRedisDatabaseProvider | ||
{ | ||
private static readonly IDatabase DeadDatabase; | ||
|
||
static TestingRedis2x1DatabaseProvider() | ||
{ | ||
var server = new RedisServer(allowAdmin: true); | ||
DeadDatabase = server.Multiplexer.GetDatabase(); | ||
using var process = Process.GetProcessById(server.ProcessId); | ||
server.Multiplexer.GetServer($"localhost:{server.Port}").Shutdown(ShutdownMode.Never); | ||
Assert.IsTrue(process.WaitForExit(5000)); | ||
} | ||
|
||
public TestingRedis2x1DatabaseProvider() | ||
: base(Enumerable.Range(0, 2).Select(i => RedisServer.GetDefaultServer(i).Multiplexer.GetDatabase()).Append(DeadDatabase)) | ||
{ | ||
} | ||
|
||
public override string CrossProcessLockTypeSuffix => "2x1"; | ||
} | ||
} | ||
using Medallion.Threading.Tests.Redis; | ||
using NUnit.Framework; | ||
using StackExchange.Redis; | ||
using StackExchange.Redis.KeyspaceIsolation; | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace Medallion.Threading.Tests.Redis | ||
{ | ||
public abstract class TestingRedisDatabaseProvider | ||
{ | ||
protected TestingRedisDatabaseProvider(IEnumerable<IDatabase> databases) | ||
{ | ||
this.Databases = databases.ToArray(); | ||
} | ||
|
||
protected TestingRedisDatabaseProvider(int count) | ||
: this(Enumerable.Range(0, count).Select(i => RedisServer.GetDefaultServer(i).Multiplexer.GetDatabase())) | ||
{ | ||
} | ||
|
||
// publicly settable so that callers can alter the dbs in use | ||
public IReadOnlyList<IDatabase> Databases { get; set; } | ||
|
||
public virtual string CrossProcessLockTypeSuffix => this.Databases.Count.ToString(); | ||
} | ||
|
||
public sealed class TestingRedisSingleDatabaseProvider : TestingRedisDatabaseProvider | ||
{ | ||
public TestingRedisSingleDatabaseProvider() : base(count: 1) { } | ||
} | ||
|
||
public sealed class TestingRedisWithKeyPrefixSingleDatabaseProvider : TestingRedisDatabaseProvider | ||
{ | ||
public TestingRedisWithKeyPrefixSingleDatabaseProvider() | ||
: base(new[] { RedisServer.GetDefaultServer(0).Multiplexer.GetDatabase().WithKeyPrefix("distributed_locks:") }) { } | ||
|
||
public override string CrossProcessLockTypeSuffix => "1WithPrefix"; | ||
} | ||
|
||
public sealed class TestingRedis3DatabaseProvider : TestingRedisDatabaseProvider | ||
{ | ||
public TestingRedis3DatabaseProvider() : base(count: 3) { } | ||
} | ||
|
||
public sealed class TestingRedis2x1DatabaseProvider : TestingRedisDatabaseProvider | ||
{ | ||
private static readonly IDatabase DeadDatabase; | ||
|
||
static TestingRedis2x1DatabaseProvider() | ||
{ | ||
var server = new RedisServer(allowAdmin: true); | ||
DeadDatabase = server.Multiplexer.GetDatabase(); | ||
using var process = Process.GetProcessById(server.ProcessId); | ||
server.Multiplexer.GetServer($"localhost:{server.Port}").Shutdown(ShutdownMode.Never); | ||
Assert.IsTrue(process.WaitForExit(5000)); | ||
} | ||
|
||
public TestingRedis2x1DatabaseProvider() | ||
: base(Enumerable.Range(0, 2).Select(i => RedisServer.GetDefaultServer(i).Multiplexer.GetDatabase()).Append(DeadDatabase)) | ||
{ | ||
} | ||
|
||
public override string CrossProcessLockTypeSuffix => "2x1"; | ||
} | ||
} |
180 changes: 90 additions & 90 deletions
180
DistributedLock.Tests/Infrastructure/Redis/TestingRedisSynchronizationStrategy.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,90 +1,90 @@ | ||
using Medallion.Threading.Internal; | ||
using Medallion.Threading.Redis; | ||
using NUnit.Framework; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using System.Threading; | ||
|
||
namespace Medallion.Threading.Tests.Redis | ||
{ | ||
public sealed class TestingRedisSynchronizationStrategy<TDatabaseProvider> : TestingSynchronizationStrategy | ||
where TDatabaseProvider : TestingRedisDatabaseProvider, new() | ||
{ | ||
private bool _preparedForHandleLost, _preparedForHandleAbandonment; | ||
private Action? _killHandleAction; | ||
private Action<RedisDistributedSynchronizationOptionsBuilder>? _options; | ||
|
||
public TDatabaseProvider DatabaseProvider { get; } = new TDatabaseProvider(); | ||
|
||
public void SetOptions(Action<RedisDistributedSynchronizationOptionsBuilder>? options) | ||
{ | ||
this._options = options; | ||
} | ||
|
||
public void Options(RedisDistributedSynchronizationOptionsBuilder options) | ||
{ | ||
if (this._preparedForHandleLost) | ||
{ | ||
options.ExtensionCadence(TimeSpan.FromMilliseconds(30)); | ||
} | ||
if (this._preparedForHandleAbandonment) | ||
{ | ||
options.Expiry(TimeSpan.FromSeconds(.2)) | ||
// the reader writer lock requires that the busy wait sleep time is shorter | ||
// than the expiry, so adjust for that | ||
.BusyWaitSleepTime(TimeSpan.FromSeconds(.01), TimeSpan.FromSeconds(.1)); | ||
} | ||
|
||
this._options?.Invoke(options); | ||
} | ||
|
||
public override IDisposable? PrepareForHandleLost() | ||
{ | ||
Invariant.Require(!this._preparedForHandleLost); | ||
this._preparedForHandleLost = true; | ||
return new HandleLostScope(this); | ||
} | ||
|
||
public override void PrepareForHandleAbandonment() => this._preparedForHandleAbandonment = true; | ||
|
||
public override void PerformAdditionalCleanupForHandleAbandonment() | ||
{ | ||
Invariant.Require(this._preparedForHandleAbandonment); | ||
Thread.Sleep(TimeSpan.FromSeconds(.5)); | ||
} | ||
|
||
public void RegisterKillHandleAction(Action action) | ||
{ | ||
if (this._preparedForHandleLost) | ||
{ | ||
this._killHandleAction += action; | ||
} | ||
} | ||
|
||
private class HandleLostScope : IDisposable | ||
{ | ||
private TestingRedisSynchronizationStrategy<TDatabaseProvider>? _strategy; | ||
|
||
public HandleLostScope(TestingRedisSynchronizationStrategy<TDatabaseProvider> strategy) | ||
{ | ||
this._strategy = strategy; | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
var strategy = Interlocked.Exchange(ref this._strategy, null); | ||
if (strategy != null) | ||
{ | ||
Invariant.Require(strategy._preparedForHandleLost); | ||
try { strategy._killHandleAction?.Invoke(); } | ||
finally | ||
{ | ||
strategy._killHandleAction = null; | ||
strategy._preparedForHandleLost = false; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
using Medallion.Threading.Internal; | ||
using Medallion.Threading.Redis; | ||
using NUnit.Framework; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using System.Threading; | ||
|
||
namespace Medallion.Threading.Tests.Redis | ||
{ | ||
public sealed class TestingRedisSynchronizationStrategy<TDatabaseProvider> : TestingSynchronizationStrategy | ||
where TDatabaseProvider : TestingRedisDatabaseProvider, new() | ||
{ | ||
private bool _preparedForHandleLost, _preparedForHandleAbandonment; | ||
private Action? _killHandleAction; | ||
private Action<RedisDistributedSynchronizationOptionsBuilder>? _options; | ||
|
||
public TDatabaseProvider DatabaseProvider { get; } = new TDatabaseProvider(); | ||
|
||
public void SetOptions(Action<RedisDistributedSynchronizationOptionsBuilder>? options) | ||
{ | ||
this._options = options; | ||
} | ||
|
||
public void Options(RedisDistributedSynchronizationOptionsBuilder options) | ||
{ | ||
if (this._preparedForHandleLost) | ||
{ | ||
options.ExtensionCadence(TimeSpan.FromMilliseconds(30)); | ||
} | ||
if (this._preparedForHandleAbandonment) | ||
{ | ||
options.Expiry(TimeSpan.FromSeconds(.2)) | ||
// the reader writer lock requires that the busy wait sleep time is shorter | ||
// than the expiry, so adjust for that | ||
.BusyWaitSleepTime(TimeSpan.FromSeconds(.01), TimeSpan.FromSeconds(.1)); | ||
} | ||
|
||
this._options?.Invoke(options); | ||
} | ||
|
||
public override IDisposable? PrepareForHandleLost() | ||
{ | ||
Invariant.Require(!this._preparedForHandleLost); | ||
this._preparedForHandleLost = true; | ||
return new HandleLostScope(this); | ||
} | ||
|
||
public override void PrepareForHandleAbandonment() => this._preparedForHandleAbandonment = true; | ||
|
||
public override void PerformAdditionalCleanupForHandleAbandonment() | ||
{ | ||
Invariant.Require(this._preparedForHandleAbandonment); | ||
Thread.Sleep(TimeSpan.FromSeconds(.5)); | ||
} | ||
|
||
public void RegisterKillHandleAction(Action action) | ||
{ | ||
if (this._preparedForHandleLost) | ||
{ | ||
this._killHandleAction += action; | ||
} | ||
} | ||
|
||
private class HandleLostScope : IDisposable | ||
{ | ||
private TestingRedisSynchronizationStrategy<TDatabaseProvider>? _strategy; | ||
|
||
public HandleLostScope(TestingRedisSynchronizationStrategy<TDatabaseProvider> strategy) | ||
{ | ||
this._strategy = strategy; | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
var strategy = Interlocked.Exchange(ref this._strategy, null); | ||
if (strategy != null) | ||
{ | ||
Invariant.Require(strategy._preparedForHandleLost); | ||
try { strategy._killHandleAction?.Invoke(); } | ||
finally | ||
{ | ||
strategy._killHandleAction = null; | ||
strategy._preparedForHandleLost = false; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.