From bf67ed38a9da3df987b0d1245bc8774f80de81cb Mon Sep 17 00:00:00 2001 From: Joes Date: Tue, 21 Oct 2025 17:34:39 +0800 Subject: [PATCH 01/21] feat: Added support for MongoDB distributed locks Added the DistributedLock.MongoDB project, providing a MongoDB-based distributed lock implementation that supports both synchronous and asynchronous lock operations. Added core classes `MongoDistributedLock`, `MongoDistributedLockHandle`, `MongoDistributedSynchronizationOptionsBuilder`, and `MongoDistributedSynchronizationProvider` to implement lock acquisition, release, automatic renewal, and configuration functions. Updated project configuration, added dependency on `MongoDB.Driver`, and supported multiple target frameworks (.NET 8, .NET Standard 2.1, .NET Framework 4.7.2). Added usage instructions and code examples in `README.md`. Added comprehensive test coverage, including basic functionality, competition scenarios, lock loss handling, and support for custom collection names, ensuring the reliability of the functionality. Updated dependency versions and optimized internal access configuration for the test project. --- src/Directory.Packages.props | 1 + src/DistributedLock.Core/packages.lock.json | 30 +- .../AssemblyAttributes.cs | 5 + .../DistributedLock.MongoDB.csproj | 62 ++ .../MongoDistributedLock.IDistributedLock.cs | 98 +++ .../MongoDistributedLock.cs | 116 +++ .../MongoDistributedLockHandle.cs | 136 ++++ ...istributedSynchronizationOptionsBuilder.cs | 124 ++++ ...MongoDistributedSynchronizationProvider.cs | 44 ++ .../MongoLockDocument.cs | 39 + .../PublicAPI.Shipped.txt | 22 + .../PublicAPI.Unshipped.txt | 1 + src/DistributedLock.MongoDB/README.md | 106 +++ .../packages.lock.json | 518 ++++++++++++++ .../packages.lock.json | 6 +- .../DistributedLock.Tests.csproj | 1 + .../MongoDB/TestingMongoDbProviders.cs | 45 ++ .../TestingMongoDbSynchronizationStrategy.cs | 11 + .../Shared/MongoDbCredentials.cs | 31 + .../Tests/MongoDB/MongoDistributedLockTest.cs | 118 +++ ...ibutedSynchronizationOptionsBuilderTest.cs | 64 ++ ...oDistributedSynchronizationProviderTest.cs | 71 ++ src/DistributedLock.Tests/packages.lock.json | 674 ++---------------- src/DistributedLock.sln | 10 +- src/DistributedLock/DistributedLock.csproj | 2 +- src/DistributedLock/packages.lock.json | 76 ++ src/DistributedLockTaker/packages.lock.json | 18 +- 27 files changed, 1783 insertions(+), 646 deletions(-) create mode 100644 src/DistributedLock.MongoDB/AssemblyAttributes.cs create mode 100644 src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj create mode 100644 src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs create mode 100644 src/DistributedLock.MongoDB/MongoDistributedLock.cs create mode 100644 src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs create mode 100644 src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs create mode 100644 src/DistributedLock.MongoDB/MongoDistributedSynchronizationProvider.cs create mode 100644 src/DistributedLock.MongoDB/MongoLockDocument.cs create mode 100644 src/DistributedLock.MongoDB/PublicAPI.Shipped.txt create mode 100644 src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt create mode 100644 src/DistributedLock.MongoDB/README.md create mode 100644 src/DistributedLock.MongoDB/packages.lock.json create mode 100644 src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbProviders.cs create mode 100644 src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbSynchronizationStrategy.cs create mode 100644 src/DistributedLock.Tests/Infrastructure/Shared/MongoDbCredentials.cs create mode 100644 src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs create mode 100644 src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationOptionsBuilderTest.cs create mode 100644 src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationProviderTest.cs diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index f0fe5922..2894c36a 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -7,6 +7,7 @@ + diff --git a/src/DistributedLock.Core/packages.lock.json b/src/DistributedLock.Core/packages.lock.json index 2f96fece..0e0884af 100644 --- a/src/DistributedLock.Core/packages.lock.json +++ b/src/DistributedLock.Core/packages.lock.json @@ -11,12 +11,6 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, - "Microsoft.CodeAnalysis.PublicApiAnalyzers": { - "type": "Direct", - "requested": "[3.3.4, )", - "resolved": "3.3.4", - "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" - }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", "requested": "[1.0.3, )", @@ -81,12 +75,6 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, - "Microsoft.CodeAnalysis.PublicApiAnalyzers": { - "type": "Direct", - "requested": "[3.3.4, )", - "resolved": "3.3.4", - "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" - }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -136,12 +124,6 @@ } }, ".NETStandard,Version=v2.1": { - "Microsoft.CodeAnalysis.PublicApiAnalyzers": { - "type": "Direct", - "requested": "[3.3.4, )", - "resolved": "3.3.4", - "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" - }, "Microsoft.SourceLink.GitHub": { "type": "Direct", "requested": "[8.0.0, )", @@ -164,17 +146,11 @@ } }, "net8.0": { - "Microsoft.CodeAnalysis.PublicApiAnalyzers": { - "type": "Direct", - "requested": "[3.3.4, )", - "resolved": "3.3.4", - "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" - }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.4, )", - "resolved": "8.0.4", - "contentHash": "PZb5nfQ+U19nhnmnR9T1jw+LTmozhuG2eeuzuW5A7DqxD/UXW2ucjmNJqnqOuh8rdPzM3MQXoF8AfFCedJdCUw==" + "requested": "[8.0.21, )", + "resolved": "8.0.21", + "contentHash": "s8H5PZQs50OcNkaB6Si54+v3GWM7vzs6vxFRMlD3aXsbM+aPCtod62gmK0BYWou9diGzmo56j8cIf/PziijDqQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/src/DistributedLock.MongoDB/AssemblyAttributes.cs b/src/DistributedLock.MongoDB/AssemblyAttributes.cs new file mode 100644 index 00000000..f3102190 --- /dev/null +++ b/src/DistributedLock.MongoDB/AssemblyAttributes.cs @@ -0,0 +1,5 @@ +using System.Runtime.CompilerServices; + +[assembly: + InternalsVisibleTo( + "DistributedLock.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fd3af56ccc8ed94fffe25bfd651e6a5674f8f20a76d37de800dd0f7380e04f0fde2da6fa200380b14fe398605b6f470c87e5e0a0bf39ae871f07536a4994aa7a0057c4d3bcedc8fef3eecb0c88c2024a1b3289305c2393acd9fb9f9a42d0bd7826738ce864d507575ea3a1fe1746ab19823303269f79379d767949807f494be8")] \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj b/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj new file mode 100644 index 00000000..828c3eaa --- /dev/null +++ b/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj @@ -0,0 +1,62 @@ + + + + netstandard2.1;net8.0;net472; + Medallion.Threading.MongoDB + True + 4 + Latest + enable + enable + + + + 1.3.0 + 1.0.0.0 + Michael Adelson + Provides a distributed lock implementation based on MongoDB + Copyright © 2020 Michael Adelson + MIT + distributed lock async mongodb + https://github.com/madelson/DistributedLock + https://github.com/madelson/DistributedLock + 1.0.0.0 + See https://github.com/madelson/DistributedLock#release-notes + true + ..\DistributedLock.snk + + + + True + True + True + + + embedded + + true + true + + + + False + 1591 + TRACE;DEBUG + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs b/src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs new file mode 100644 index 00000000..89936ac7 --- /dev/null +++ b/src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs @@ -0,0 +1,98 @@ +using Medallion.Threading.Internal; + +namespace Medallion.Threading.MongoDB; + +public partial class MongoDistributedLock +{ + IDistributedSynchronizationHandle? IDistributedLock.TryAcquire(TimeSpan timeout, CancellationToken cancellationToken) + { + return TryAcquire(timeout, cancellationToken); + } + + IDistributedSynchronizationHandle IDistributedLock.Acquire(TimeSpan? timeout, CancellationToken cancellationToken) + { + return Acquire(timeout, cancellationToken); + } + + ValueTask IDistributedLock.TryAcquireAsync(TimeSpan timeout, CancellationToken cancellationToken) + { + return TryAcquireAsync(timeout, cancellationToken).Convert(To.ValueTask); + } + + ValueTask IDistributedLock.AcquireAsync(TimeSpan? timeout, CancellationToken cancellationToken) + { + return AcquireAsync(timeout, cancellationToken).Convert(To.ValueTask); + } + + /// + /// Attempts to acquire the lock synchronously. Usage: + /// + /// using (var handle = myLock.TryAcquire(...)) + /// { + /// if (handle != null) { /* we have the lock! */ } + /// } + /// // dispose releases the lock if we took it + /// + /// + /// How long to wait before giving up on the acquisition attempt. Defaults to 0 + /// Specifies a token by which the wait can be canceled + /// A which can be used to release the lock or null on failure + public MongoDistributedLockHandle? TryAcquire(TimeSpan timeout = default, CancellationToken cancellationToken = default) + { + return DistributedLockHelpers.TryAcquire(this, timeout, cancellationToken); + } + + /// + /// Acquires the lock synchronously, failing with if the attempt times out. Usage: + /// + /// using (myLock.Acquire(...)) + /// { + /// /* we have the lock! */ + /// } + /// // dispose releases the lock + /// + /// + /// How long to wait before giving up on the acquisition attempt. Defaults to + /// Specifies a token by which the wait can be canceled + /// A which can be used to release the lock + public MongoDistributedLockHandle Acquire(TimeSpan? timeout = null, CancellationToken cancellationToken = default) + { + return DistributedLockHelpers.Acquire(this, timeout, cancellationToken); + } + + /// + /// Attempts to acquire the lock asynchronously. Usage: + /// + /// await using (var handle = await myLock.TryAcquireAsync(...)) + /// { + /// if (handle != null) { /* we have the lock! */ } + /// } + /// // dispose releases the lock if we took it + /// + /// + /// How long to wait before giving up on the acquisition attempt. Defaults to 0 + /// Specifies a token by which the wait can be canceled + /// A which can be used to release the lock or null on failure + public ValueTask TryAcquireAsync(TimeSpan timeout = default, CancellationToken cancellationToken = default) + { + return this.As>().InternalTryAcquireAsync(timeout, cancellationToken); + } + + /// + /// Acquires the lock asynchronously, failing with if the attempt times out. Usage: + /// + /// await using (await myLock.AcquireAsync(...)) + /// { + /// /* we have the lock! */ + /// } + /// // dispose releases the lock + /// + /// + /// How long to wait before giving up on the acquisition attempt. Defaults to + /// Specifies a token by which the wait can be canceled + /// A which can be used to release the lock + public ValueTask AcquireAsync(TimeSpan? timeout = null, CancellationToken cancellationToken = default) + { + return DistributedLockHelpers.AcquireAsync(this, timeout, cancellationToken); + } +} \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoDistributedLock.cs b/src/DistributedLock.MongoDB/MongoDistributedLock.cs new file mode 100644 index 00000000..1fb4ec8d --- /dev/null +++ b/src/DistributedLock.MongoDB/MongoDistributedLock.cs @@ -0,0 +1,116 @@ +using Medallion.Threading.Internal; +using MongoDB.Driver; + +namespace Medallion.Threading.MongoDB; + +/// +/// Implements a using MongoDB. +/// +public sealed partial class MongoDistributedLock : IInternalDistributedLock +{ + private readonly string _collectionName; + private readonly IMongoDatabase _database; + private readonly MongoDistributedLockOptions _options; + + /// + /// The MongoDB key used to implement the lock + /// + public string Key { get; } + + /// + /// Implements + /// + public string Name => Key; + + /// + /// Constructs a lock named using the provided and . + /// The locks will be stored in a collection named "DistributedLocks" by default. + /// + public MongoDistributedLock(string key, IMongoDatabase database, Action? options = null) + : this(key, database, "DistributedLocks", options) { } + + /// + /// Constructs a lock named using the provided , , and + /// . + /// + public MongoDistributedLock(string key, IMongoDatabase database, string collectionName, Action? options = null) + { + if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException(nameof(key)); } + _database = database ?? throw new ArgumentNullException(nameof(database)); + _collectionName = collectionName ?? throw new ArgumentNullException(nameof(collectionName)); + Key = key; + _options = MongoDistributedSynchronizationOptionsBuilder.GetOptions(options); + } + + ValueTask IInternalDistributedLock.InternalTryAcquireAsync(TimeoutValue timeout, CancellationToken cancellationToken) + { + return BusyWaitHelper.WaitAsync(this, + (@this, ct) => @this.TryAcquireAsync(ct), + timeout, + _options.MinBusyWaitSleepTime, + _options.MaxBusyWaitSleepTime, + cancellationToken); + } + + private async ValueTask TryAcquireAsync(CancellationToken cancellationToken) + { + var collection = _database.GetCollection(_collectionName); + + // Ensure index exists for efficient queries + await EnsureIndexAsync(collection, cancellationToken).ConfigureAwait(false); + var lockId = Guid.NewGuid().ToString(); + var now = DateTime.UtcNow; + var expiresAt = now.Add(_options.Expiry.TimeSpan); + + // Filter: lock with this key doesn't exist OR has expired + var filter = Builders.Filter.Or(Builders.Filter.Eq(d => d.Id, Key) & Builders.Filter.Lt(d => d.ExpiresAt, now), + Builders.Filter.Eq(d => d.Id, Key) & Builders.Filter.Exists(d => d.ExpiresAt, false)); + var update = Builders.Update + .Set(d => d.Id, Key) + .Set(d => d.LockId, lockId) + .Set(d => d.ExpiresAt, expiresAt) + .Set(d => d.AcquiredAt, now); + var options = new FindOneAndUpdateOptions { IsUpsert = true, ReturnDocument = ReturnDocument.After }; + try + { + var result = await collection.FindOneAndUpdateAsync(filter, update, options, cancellationToken).ConfigureAwait(false); + + // Verify we actually got the lock + if (result != null && result.LockId == lockId) + { + return new(_database, + _collectionName, + Key, + lockId, + _options.Expiry, + _options.ExtensionCadence); + } + } + catch (MongoWriteException ex) when (ex.WriteError?.Category == ServerErrorCategory.DuplicateKey) + { + // Lock is already held by someone else + return null; + } + catch (MongoCommandException) + { + // Lock is already held + return null; + } + return null; + } + + private static async Task EnsureIndexAsync(IMongoCollection collection, CancellationToken cancellationToken) + { + try + { + var indexKeys = Builders.IndexKeys.Ascending(d => d.ExpiresAt); + var indexOptions = new CreateIndexOptions { Background = true }; + var indexModel = new CreateIndexModel(indexKeys, indexOptions); + await collection.Indexes.CreateOneAsync(indexModel, cancellationToken: cancellationToken).ConfigureAwait(false); + } + catch + { + // Index might already exist, ignore errors + } + } +} \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs b/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs new file mode 100644 index 00000000..352c698c --- /dev/null +++ b/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs @@ -0,0 +1,136 @@ +using Medallion.Threading.Internal; +using MongoDB.Driver; + +namespace Medallion.Threading.MongoDB; + +/// +/// Implements for +/// +public sealed class MongoDistributedLockHandle : IDistributedSynchronizationHandle +{ + private readonly string _collectionName; + private readonly CancellationTokenSource _cts; + private readonly IMongoDatabase _database; + private readonly Task _extensionTask; + private readonly string _key; + private readonly string _lockId; + private int _disposed; + + /// + /// Implements + /// + public CancellationToken HandleLostToken => _cts.Token; + + internal MongoDistributedLockHandle( + IMongoDatabase database, + string collectionName, + string key, + string lockId, + TimeoutValue expiry, + TimeoutValue extensionCadence) + { + _database = database; + _collectionName = collectionName; + _key = key; + _lockId = lockId; + _cts = new(); + + // Start background task to extend the lock + _extensionTask = ExtendLockAsync(expiry, extensionCadence, _cts.Token); + } + + /// + /// Releases the lock + /// + public void Dispose() + { + if (Interlocked.Exchange(ref _disposed, 1) is not 0) + { + return; + } + _cts.Cancel(); + try + { + _extensionTask.Wait(HandleLostToken); + } + catch + { + // Ignore exceptions during cleanup + } + finally + { + _cts.Dispose(); + ReleaseLockAsync(CancellationToken.None).AsTask().Wait(HandleLostToken); + } + } + + /// + /// Releases the lock asynchronously + /// + public async ValueTask DisposeAsync() + { + if (Interlocked.Exchange(ref _disposed, 1) is 0) + { + _cts.Cancel(); + try + { + await _extensionTask.ConfigureAwait(false); + } + catch + { + // Ignore exceptions during cleanup + } + finally + { + _cts.Dispose(); + await ReleaseLockAsync(CancellationToken.None).ConfigureAwait(false); + } + } + } + + private async Task ExtendLockAsync(TimeoutValue expiry, TimeoutValue extensionCadence, CancellationToken cancellationToken) + { + try + { + while (!cancellationToken.IsCancellationRequested) + { + await Task.Delay(extensionCadence.TimeSpan, cancellationToken).ConfigureAwait(false); + var collection = _database.GetCollection(_collectionName); + var filter = Builders.Filter.Eq(d => d.Id, _key) & Builders.Filter.Eq(d => d.LockId, _lockId); + var update = Builders.Update.Set(d => d.ExpiresAt, DateTime.UtcNow.Add(expiry.TimeSpan)); + var result = await collection.UpdateOneAsync(filter, update, cancellationToken: cancellationToken).ConfigureAwait(false); + + // If we failed to extend, the lock was lost + if (result.MatchedCount is not 0) + { + continue; + } + _cts.Cancel(); + break; + } + } + catch (OperationCanceledException) + { + // Expected when disposing + } + catch + { + // Lock extension failed, signal that the lock is lost + _cts.Cancel(); + } + } + + private async ValueTask ReleaseLockAsync(CancellationToken cancellationToken) + { + try + { + var collection = _database.GetCollection(_collectionName); + var filter = Builders.Filter.Eq(d => d.Id, _key) & Builders.Filter.Eq(d => d.LockId, _lockId); + await collection.DeleteOneAsync(filter, cancellationToken).ConfigureAwait(false); + } + catch + { + // Ignore errors during release + } + } +} \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs b/src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs new file mode 100644 index 00000000..eae699e1 --- /dev/null +++ b/src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs @@ -0,0 +1,124 @@ +using Medallion.Threading.Internal; + +namespace Medallion.Threading.MongoDB; + +/// +/// Options for configuring a MongoDB-based distributed synchronization algorithm +/// +public sealed class MongoDistributedSynchronizationOptionsBuilder +{ + internal static readonly TimeoutValue DefaultExpiry = TimeSpan.FromSeconds(30); + + /// + /// We don't want to allow expiry to go too low, since then the lock doesn't even work + /// + internal static readonly TimeoutValue MinimumExpiry = TimeSpan.FromSeconds(.1); + + private TimeoutValue? _expiry, _extensionCadence, _minBusyWaitSleepTime, _maxBusyWaitSleepTime; + + internal MongoDistributedSynchronizationOptionsBuilder() { } + + /// + /// Specifies how long the lock will last, absent auto-extension. Because auto-extension exists, + /// this value generally will have little effect on program behavior. However, making the expiry longer means that + /// auto-extension requests can occur less frequently, saving resources. On the other hand, when a lock is abandoned + /// without explicit release (e. g. if the holding process crashes), the expiry determines how long other processes + /// would need to wait in order to acquire it. + /// Defaults to 30s. + /// + public MongoDistributedSynchronizationOptionsBuilder Expiry(TimeSpan expiry) + { + var expiryTimeoutValue = new TimeoutValue(expiry, nameof(expiry)); + if (expiryTimeoutValue.IsInfinite || expiryTimeoutValue.CompareTo(MinimumExpiry) < 0) + { + throw new ArgumentOutOfRangeException(nameof(expiry), expiry, $"Must be >= {MinimumExpiry.TimeSpan} and < ∞"); + } + _expiry = expiryTimeoutValue; + return this; + } + + /// + /// Determines how frequently the lock will be extended while held. More frequent extension means more unnecessary requests + /// but also a lower chance of losing the lock due to the process hanging or otherwise failing to get its extension request in + /// before the lock expiry elapses. + /// Defaults to 1/3 of the expiry time. + /// + public MongoDistributedSynchronizationOptionsBuilder ExtensionCadence(TimeSpan extensionCadence) + { + _extensionCadence = new TimeoutValue(extensionCadence, nameof(extensionCadence)); + return this; + } + + /// + /// Waiting to acquire a lock requires a busy wait that alternates acquire attempts and sleeps. + /// This determines how much time is spent sleeping between attempts. Lower values will raise the + /// volume of acquire requests under contention but will also raise the responsiveness (how long + /// it takes a waiter to notice that a contended the lock has become available). + /// Specifying a range of values allows the implementation to select an actual value in the range + /// at random for each sleep. This helps avoid the case where two clients become "synchronized" + /// in such a way that results in one client monopolizing the lock. + /// The default is [10ms, 800ms] + /// + public MongoDistributedSynchronizationOptionsBuilder BusyWaitSleepTime(TimeSpan min, TimeSpan max) + { + var minTimeoutValue = new TimeoutValue(min, nameof(min)); + var maxTimeoutValue = new TimeoutValue(max, nameof(max)); + if (minTimeoutValue.IsInfinite) { throw new ArgumentOutOfRangeException(nameof(min), "may not be infinite"); } + if (maxTimeoutValue.IsInfinite || maxTimeoutValue.CompareTo(min) < 0) + { + throw new ArgumentOutOfRangeException(nameof(max), max, "must be non-infinite and greater than " + nameof(min)); + } + _minBusyWaitSleepTime = minTimeoutValue; + _maxBusyWaitSleepTime = maxTimeoutValue; + return this; + } + + internal static MongoDistributedLockOptions GetOptions(Action? optionsBuilder) + { + MongoDistributedSynchronizationOptionsBuilder? options; + if (optionsBuilder != null) + { + options = new(); + optionsBuilder(options); + } + else + { + options = null; + } + var expiry = options?._expiry ?? DefaultExpiry; + TimeoutValue extensionCadence; + if (options?._extensionCadence is { } specifiedExtensionCadence) + { + if (specifiedExtensionCadence.CompareTo(expiry) >= 0) + { + throw new ArgumentOutOfRangeException(nameof(extensionCadence), + specifiedExtensionCadence.TimeSpan, + $"{nameof(extensionCadence)} must be less than {nameof(expiry)} ({expiry.TimeSpan})"); + } + extensionCadence = specifiedExtensionCadence; + } + else + { + extensionCadence = TimeSpan.FromMilliseconds(expiry.InMilliseconds / 3.0); + } + return new(expiry, + extensionCadence, + options?._minBusyWaitSleepTime ?? TimeSpan.FromMilliseconds(10), + options?._maxBusyWaitSleepTime ?? TimeSpan.FromSeconds(0.8)); + } +} + +internal readonly struct MongoDistributedLockOptions( + TimeoutValue expiry, + TimeoutValue extensionCadence, + TimeoutValue minBusyWaitSleepTime, + TimeoutValue maxBusyWaitSleepTime) +{ + public TimeoutValue Expiry { get; } = expiry; + + public TimeoutValue ExtensionCadence { get; } = extensionCadence; + + public TimeoutValue MinBusyWaitSleepTime { get; } = minBusyWaitSleepTime; + + public TimeoutValue MaxBusyWaitSleepTime { get; } = maxBusyWaitSleepTime; +} \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoDistributedSynchronizationProvider.cs b/src/DistributedLock.MongoDB/MongoDistributedSynchronizationProvider.cs new file mode 100644 index 00000000..c3a46ace --- /dev/null +++ b/src/DistributedLock.MongoDB/MongoDistributedSynchronizationProvider.cs @@ -0,0 +1,44 @@ +using MongoDB.Driver; + +namespace Medallion.Threading.MongoDB; + +/// +/// Implements for . +/// +public sealed class MongoDistributedSynchronizationProvider : IDistributedLockProvider +{ + private readonly string _collectionName; + private readonly IMongoDatabase _database; + private readonly Action? _options; + + /// + /// Constructs a that connects to the provided + /// and uses the provided . Locks will be stored in a collection named "DistributedLocks" by default. + /// + public MongoDistributedSynchronizationProvider(IMongoDatabase database, Action? options = null) + : this(database, "DistributedLocks", options) { } + + /// + /// Constructs a that connects to the provided , + /// stores locks in the specified , and uses the provided . + /// + public MongoDistributedSynchronizationProvider(IMongoDatabase database, string collectionName, Action? options = null) + { + _database = database ?? throw new ArgumentNullException(nameof(database)); + _collectionName = collectionName ?? throw new ArgumentNullException(nameof(collectionName)); + _options = options; + } + + /// + /// Creates a using the given . + /// + public MongoDistributedLock CreateLock(string name) + { + return new(name, _database, _collectionName, _options); + } + + IDistributedLock IDistributedLockProvider.CreateLock(string name) + { + return CreateLock(name); + } +} \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoLockDocument.cs b/src/DistributedLock.MongoDB/MongoLockDocument.cs new file mode 100644 index 00000000..d6359a29 --- /dev/null +++ b/src/DistributedLock.MongoDB/MongoLockDocument.cs @@ -0,0 +1,39 @@ +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace Medallion.Threading.MongoDB; + +/// +/// Represents a lock document stored in MongoDB +/// +// ReSharper disable once ClassNeverInstantiated.Global +internal sealed class MongoLockDocument +{ + /// + /// When the lock was acquired (UTC) + /// + [BsonElement("acquiredAt")] + [BsonRepresentation(BsonType.DateTime)] + public DateTime AcquiredAt { get; set; } + + /// + /// When the lock expires (UTC) + /// + [BsonElement("expiresAt")] + [BsonRepresentation(BsonType.DateTime)] + public DateTime ExpiresAt { get; set; } + + /// + /// The lock name/key (MongoDB document ID) + /// + [BsonId] + [BsonRepresentation(BsonType.String)] + public string Id { get; set; } = string.Empty; + + /// + /// Unique identifier for this lock acquisition + /// + [BsonElement("lockId")] + [BsonRepresentation(BsonType.String)] + public string LockId { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/PublicAPI.Shipped.txt b/src/DistributedLock.MongoDB/PublicAPI.Shipped.txt new file mode 100644 index 00000000..5e440dc2 --- /dev/null +++ b/src/DistributedLock.MongoDB/PublicAPI.Shipped.txt @@ -0,0 +1,22 @@ +#nullable enable +Medallion.Threading.MongoDB.MongoDistributedLock +Medallion.Threading.MongoDB.MongoDistributedLock.Acquire(System.TimeSpan? timeout = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Medallion.Threading.MongoDB.MongoDistributedLockHandle! +Medallion.Threading.MongoDB.MongoDistributedLock.AcquireAsync(System.TimeSpan? timeout = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +Medallion.Threading.MongoDB.MongoDistributedLock.Key.get -> string! +Medallion.Threading.MongoDB.MongoDistributedLock.Name.get -> string! +Medallion.Threading.MongoDB.MongoDistributedLock.MongoDistributedLock(string! key, MongoDB.Driver.IMongoDatabase! database, System.Action? options = null) -> void +Medallion.Threading.MongoDB.MongoDistributedLock.MongoDistributedLock(string! key, MongoDB.Driver.IMongoDatabase! database, string! collectionName, System.Action? options = null) -> void +Medallion.Threading.MongoDB.MongoDistributedLock.TryAcquire(System.TimeSpan timeout = default(System.TimeSpan), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Medallion.Threading.MongoDB.MongoDistributedLockHandle? +Medallion.Threading.MongoDB.MongoDistributedLock.TryAcquireAsync(System.TimeSpan timeout = default(System.TimeSpan), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask +Medallion.Threading.MongoDB.MongoDistributedLockHandle +Medallion.Threading.MongoDB.MongoDistributedLockHandle.Dispose() -> void +Medallion.Threading.MongoDB.MongoDistributedLockHandle.DisposeAsync() -> System.Threading.Tasks.ValueTask +Medallion.Threading.MongoDB.MongoDistributedLockHandle.HandleLostToken.get -> System.Threading.CancellationToken +Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder +Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder.BusyWaitSleepTime(System.TimeSpan min, System.TimeSpan max) -> Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder! +Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder.Expiry(System.TimeSpan expiry) -> Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder! +Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder.ExtensionCadence(System.TimeSpan extensionCadence) -> Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder! +Medallion.Threading.MongoDB.MongoDistributedSynchronizationProvider +Medallion.Threading.MongoDB.MongoDistributedSynchronizationProvider.CreateLock(string! name) -> Medallion.Threading.MongoDB.MongoDistributedLock! +Medallion.Threading.MongoDB.MongoDistributedSynchronizationProvider.MongoDistributedSynchronizationProvider(MongoDB.Driver.IMongoDatabase! database, System.Action? options = null) -> void +Medallion.Threading.MongoDB.MongoDistributedSynchronizationProvider.MongoDistributedSynchronizationProvider(MongoDB.Driver.IMongoDatabase! database, string! collectionName, System.Action? options = null) -> void diff --git a/src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt b/src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt new file mode 100644 index 00000000..1883be4e --- /dev/null +++ b/src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +Medallion.Threading.MongoDB.MongoDistributedLock \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/README.md b/src/DistributedLock.MongoDB/README.md new file mode 100644 index 00000000..2cac9f0d --- /dev/null +++ b/src/DistributedLock.MongoDB/README.md @@ -0,0 +1,106 @@ +# DistributedLock.MongoDB + +This library provides distributed lock implementation using MongoDB as the backing store. + +## Installation + +```bash +dotnet add package DistributedLock.MongoDB +``` + +## Usage + +### Basic Lock Usage + +```csharp +using Medallion.Threading.MongoDB; +using MongoDB.Driver; + +// Create MongoDB client and database +var client = new MongoClient("mongodb://localhost:27017"); +var database = client.GetDatabase("myDatabase"); + +// Create a lock +var @lock = new MongoDistributedLock("myLockName", database); + +// Acquire the lock +await using (var handle = await @lock.AcquireAsync()) +{ + // Critical section protected by the lock + Console.WriteLine("Lock acquired!"); +} +// Lock is automatically released when disposed +``` + +### Using the Provider + +```csharp +using Medallion.Threading.MongoDB; +using MongoDB.Driver; + +var client = new MongoClient("mongodb://localhost:27017"); +var database = client.GetDatabase("myDatabase"); + +// Create a provider +var provider = new MongoDistributedSynchronizationProvider(database); + +// Use the provider to create locks +var lock1 = provider.CreateLock("lock1"); +var lock2 = provider.CreateLock("lock2"); + +await using (var handle = await lock1.AcquireAsync()) +{ + // Do work... +} +``` + +### Configuration Options + +You can customize the lock behavior using the options builder: + +```csharp +var @lock = new MongoDistributedLock( + "myLockName", + database, + options => options + .Expiry(TimeSpan.FromSeconds(30)) // Lock expiry time + .ExtensionCadence(TimeSpan.FromSeconds(10)) // How often to extend the lock + .BusyWaitSleepTime( // Sleep time between acquire attempts + min: TimeSpan.FromMilliseconds(10), + max: TimeSpan.FromMilliseconds(800)) +); +``` + +### Custom Collection Name + +By default, locks are stored in a collection named "DistributedLocks". You can specify a custom collection name: + +```csharp +var @lock = new MongoDistributedLock("myLockName", database, "MyCustomLocks"); +``` + +## How It Works + +The MongoDB distributed lock uses MongoDB's document upsert and update operations to implement distributed locking: + +1. **Acquisition**: Attempts to insert or update a document with the lock key and a unique lock ID +2. **Extension**: Automatically extends the lock expiry while held to prevent timeout +3. **Release**: Deletes the lock document when disposed +4. **Expiry**: Locks automatically expire if not extended, allowing recovery from crashed processes + +## Features + +- ✅ Async/await support +- ✅ Automatic lock extension while held +- ✅ Configurable expiry and extension cadence +- ✅ Lock abandonment protection via expiry +- ✅ `CancellationToken` support +- ✅ Handle lost token notification +- ✅ Multi-target support: .NET 8, .NET Standard 2.1, .NET Framework 4.7.2 + +## Notes + +- The lock collection will have an index on the `expiresAt` field for efficient queries +- Lock extension happens automatically in the background +- If lock extension fails, the `HandleLostToken` will be signaled +- Stale locks (from crashed processes) will automatically expire based on the expiry setting diff --git a/src/DistributedLock.MongoDB/packages.lock.json b/src/DistributedLock.MongoDB/packages.lock.json new file mode 100644 index 00000000..11abd24a --- /dev/null +++ b/src/DistributedLock.MongoDB/packages.lock.json @@ -0,0 +1,518 @@ +{ + "version": 2, + "dependencies": { + ".NETFramework,Version=v4.7.2": { + "Microsoft.CodeAnalysis.PublicApiAnalyzers": { + "type": "Direct", + "requested": "[3.3.4, )", + "resolved": "3.3.4", + "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "MongoDB.Driver": { + "type": "Direct", + "requested": "[3.5.0, )", + "resolved": "3.5.0", + "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "System.Net.Http": "4.3.4", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "ZstdSharp.Port": "0.7.3" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0", + "System.Buffers": "4.5.1" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", + "dependencies": { + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.0", + "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Text.Encoding.CodePages": "5.0.0" + } + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "4.7.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.4", + "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", + "dependencies": { + "System.Security.Cryptography.X509Certificates": "4.3.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==" + }, + "System.Security.Cryptography.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==" + }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "NyscU59xX6Uo91qvhOs2Ccho3AR2TnZPomo1Z0K6YpyztBPM/A5VbkzOO19sy3A3i1TtEnTxA7bCe3Us+r5MWg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Memory": "4.5.5" + } + }, + "distributedlock.core": { + "type": "Project", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "[8.0.0, )", + "System.ValueTuple": "[4.5.0, )" + } + } + }, + ".NETStandard,Version=v2.1": { + "Microsoft.CodeAnalysis.PublicApiAnalyzers": { + "type": "Direct", + "requested": "[3.3.4, )", + "resolved": "3.3.4", + "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "MongoDB.Driver": { + "type": "Direct", + "requested": "[3.5.0, )", + "resolved": "3.5.0", + "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.0", + "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==", + "dependencies": { + "System.Text.Encoding.CodePages": "5.0.0" + } + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.7.1" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "NyscU59xX6Uo91qvhOs2Ccho3AR2TnZPomo1Z0K6YpyztBPM/A5VbkzOO19sy3A3i1TtEnTxA7bCe3Us+r5MWg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "distributedlock.core": { + "type": "Project" + } + }, + "net8.0": { + "Microsoft.CodeAnalysis.PublicApiAnalyzers": { + "type": "Direct", + "requested": "[3.3.4, )", + "resolved": "3.3.4", + "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" + }, + "Microsoft.NET.ILLink.Tasks": { + "type": "Direct", + "requested": "[8.0.21, )", + "resolved": "8.0.21", + "contentHash": "s8H5PZQs50OcNkaB6Si54+v3GWM7vzs6vxFRMlD3aXsbM+aPCtod62gmK0BYWou9diGzmo56j8cIf/PziijDqQ==" + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "MongoDB.Driver": { + "type": "Direct", + "requested": "[3.5.0, )", + "resolved": "3.5.0", + "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==" + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.0", + "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, + "distributedlock.core": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/src/DistributedLock.Postgres/packages.lock.json b/src/DistributedLock.Postgres/packages.lock.json index 797320b6..041eb72b 100644 --- a/src/DistributedLock.Postgres/packages.lock.json +++ b/src/DistributedLock.Postgres/packages.lock.json @@ -518,9 +518,9 @@ }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.4, )", - "resolved": "8.0.4", - "contentHash": "PZb5nfQ+U19nhnmnR9T1jw+LTmozhuG2eeuzuW5A7DqxD/UXW2ucjmNJqnqOuh8rdPzM3MQXoF8AfFCedJdCUw==" + "requested": "[8.0.21, )", + "resolved": "8.0.21", + "contentHash": "s8H5PZQs50OcNkaB6Si54+v3GWM7vzs6vxFRMlD3aXsbM+aPCtod62gmK0BYWou9diGzmo56j8cIf/PziijDqQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/src/DistributedLock.Tests/DistributedLock.Tests.csproj b/src/DistributedLock.Tests/DistributedLock.Tests.csproj index be82219e..07020675 100644 --- a/src/DistributedLock.Tests/DistributedLock.Tests.csproj +++ b/src/DistributedLock.Tests/DistributedLock.Tests.csproj @@ -31,6 +31,7 @@ + \ No newline at end of file diff --git a/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbProviders.cs b/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbProviders.cs new file mode 100644 index 00000000..d873e300 --- /dev/null +++ b/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbProviders.cs @@ -0,0 +1,45 @@ +using Medallion.Threading.MongoDB; +using MongoDB.Driver; + +namespace Medallion.Threading.Tests.MongoDB; + +public sealed class TestingMongoDistributedLockProvider : TestingLockProvider +{ + private readonly string _collectionName = "DistributedLocks_" + Guid.NewGuid().ToString("N"); + private readonly IMongoDatabase _database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + + public override IDistributedLock CreateLockWithExactName(string name) + { + var @lock = new MongoDistributedLock(name, _database, _collectionName); + Strategy.KillHandleAction = () => + { + var collection = _database.GetCollection(_collectionName); + collection.DeleteOne(Builders.Filter.Eq(d => d.Id, name)); + }; + return @lock; + } + + public override string GetSafeName(string name) + { + return new MongoDistributedLock(name, _database, _collectionName).Name; + } + + public override string GetCrossProcessLockType() + { + return nameof(MongoDistributedLock); + } + + public override void Dispose() + { + // Clean up test collection + try + { + _database.DropCollection(_collectionName); + } + catch + { + // Ignore cleanup errors + } + base.Dispose(); + } +} \ No newline at end of file diff --git a/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbSynchronizationStrategy.cs b/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbSynchronizationStrategy.cs new file mode 100644 index 00000000..4324c1d5 --- /dev/null +++ b/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbSynchronizationStrategy.cs @@ -0,0 +1,11 @@ +namespace Medallion.Threading.Tests.MongoDB; + +public sealed class TestingMongoDbSynchronizationStrategy : TestingSynchronizationStrategy +{ + public Action? KillHandleAction { get; set; } + + public override void PrepareForHandleAbandonment() + { + KillHandleAction?.Invoke(); + } +} \ No newline at end of file diff --git a/src/DistributedLock.Tests/Infrastructure/Shared/MongoDbCredentials.cs b/src/DistributedLock.Tests/Infrastructure/Shared/MongoDbCredentials.cs new file mode 100644 index 00000000..8c8ce172 --- /dev/null +++ b/src/DistributedLock.Tests/Infrastructure/Shared/MongoDbCredentials.cs @@ -0,0 +1,31 @@ +using MongoDB.Driver; +using System.IO; + +namespace Medallion.Threading.Tests.MongoDB; + +internal static class MongoDbCredentials +{ + private static string? _connectionString; + + public static string GetConnectionString(string baseDirectory) + { + if (_connectionString != null) { return _connectionString; } + var file = Path.GetFullPath(Path.Combine(baseDirectory, "..", "..", "..", "credentials", "mongodb.txt")); + if (File.Exists(file)) + { + _connectionString = File.ReadAllText(file).Trim(); + } + else + { + // Default local MongoDB connection + _connectionString = "mongodb://localhost:27017"; + } + return _connectionString; + } + + public static IMongoDatabase GetDefaultDatabase(string baseDirectory) + { + var client = new MongoClient(GetConnectionString(baseDirectory)); + return client.GetDatabase("DistributedLockTests"); + } +} \ No newline at end of file diff --git a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs new file mode 100644 index 00000000..a8f8ef1e --- /dev/null +++ b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs @@ -0,0 +1,118 @@ +using Medallion.Threading.MongoDB; +using MongoDB.Driver; +using Moq; +using NUnit.Framework; + +namespace Medallion.Threading.Tests.MongoDB; + +public class MongoDistributedLockTest +{ + [Test] + public async Task TestBasicLockFunctionality() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + var lockName = TestHelper.UniqueName; + var @lock = new MongoDistributedLock(lockName, database); + await using (var handle = await @lock.AcquireAsync()) + { + Assert.That(handle, Is.Not.Null); + // Use async TryAcquireAsync instead of synchronous IsHeld() + var handle2 = await @lock.TryAcquireAsync(TimeSpan.Zero); + Assert.That(handle2, Is.Null, "Lock should be held"); + if (handle2 != null) + { + await handle2.DisposeAsync(); + } + } + // Verify lock is released + await using (var handle = await @lock.TryAcquireAsync(TimeSpan.FromSeconds(1))) + { + Assert.That(handle, Is.Not.Null, "Lock should be released"); + } + } + + [Test] + public async Task TestCustomCollectionName() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + var lockName = TestHelper.UniqueName; + const string CustomCollectionName = "CustomLocks"; + var @lock = new MongoDistributedLock(lockName, database, CustomCollectionName); + await using (var handle = await @lock.AcquireAsync()) + { + Assert.That(handle, Is.Not.Null); + } + + // Verify the collection was created + var collectionExists = (await database.ListCollectionNamesAsync()).ToList().Contains(CustomCollectionName); + Assert.That(collectionExists, Is.True); + + // Cleanup + await database.DropCollectionAsync(CustomCollectionName); + } + + [Test] + public async Task TestHandleLostToken() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + var lockName = TestHelper.UniqueName; + // Configure a short extension cadence so the test doesn't have to wait too long + var @lock = new MongoDistributedLock(lockName, database, options: o => o.ExtensionCadence(TimeSpan.FromMilliseconds(500))); + await using var handle = await @lock.AcquireAsync(); + Assert.That(handle, Is.Not.Null); + Assert.Multiple(() => + { + Assert.That(handle.HandleLostToken.CanBeCanceled, Is.True); + Assert.That(handle.HandleLostToken.IsCancellationRequested, Is.False); + }); + + // Manually delete the lock document to simulate lock loss + var collection = database.GetCollection("DistributedLocks"); + await collection.DeleteOneAsync(Builders.Filter.Eq(d => d.Id, lockName)); + + // Wait a bit for the extension task to detect the loss + await Task.Delay(TimeSpan.FromSeconds(2)); + Assert.That(handle.HandleLostToken.IsCancellationRequested, Is.True, "HandleLostToken should be signaled when lock is lost"); + } + + [Test] + public async Task TestLockContentionAsync() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + var lockName = TestHelper.UniqueName; + var lock1 = new MongoDistributedLock(lockName, database); + var lock2 = new MongoDistributedLock(lockName, database); + await using (var handle1 = await lock1.AcquireAsync()) + { + Assert.That(handle1, Is.Not.Null); + var handle2 = await lock2.TryAcquireAsync(TimeSpan.FromMilliseconds(100)); + Assert.That(handle2, Is.Null, "Should not acquire lock while held by another instance"); + } + + // After release, lock2 should be able to acquire + await using (var handle2 = await lock2.AcquireAsync(TimeSpan.FromSeconds(5))) + { + Assert.That(handle2, Is.Not.Null); + } + } + + [Test] + public void TestName() + { + const string Name = "\0🐉汉字\b\r\n\\"; + var database = new Mock(MockBehavior.Strict).Object; + var @lock = new MongoDistributedLock(Name, database); + @lock.Name.ShouldEqual(Name); + @lock.Key.ShouldEqual(Name); + } + + [Test] + public void TestValidatesConstructorParameters() + { + var database = new Mock(MockBehavior.Strict).Object; + Assert.Throws(() => new MongoDistributedLock(null!, database)); + Assert.Throws(() => new MongoDistributedLock(string.Empty, database)); + Assert.Throws(() => new MongoDistributedLock("key", null!)); + Assert.Throws(() => new MongoDistributedLock("key", database, (string)null!)); + } +} \ No newline at end of file diff --git a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationOptionsBuilderTest.cs b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationOptionsBuilderTest.cs new file mode 100644 index 00000000..c6d4a430 --- /dev/null +++ b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationOptionsBuilderTest.cs @@ -0,0 +1,64 @@ +using Medallion.Threading.MongoDB; +using NUnit.Framework; + +namespace Medallion.Threading.Tests.MongoDB; + +[Category("CI")] +public class MongoDistributedSynchronizationOptionsBuilderTest +{ + [Test] + public void TestBusyWaitSleepTimeValidation() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + Assert.Throws(() => + new MongoDistributedLock("test", database, options => options + .BusyWaitSleepTime(TimeSpan.FromSeconds(1), TimeSpan.FromMilliseconds(500)))); + Assert.Throws(() => + new MongoDistributedLock("test", database, options => options + .BusyWaitSleepTime(Timeout.InfiniteTimeSpan, TimeSpan.FromSeconds(1)))); + Assert.DoesNotThrow(() => + new MongoDistributedLock("test", database, options => options + .BusyWaitSleepTime(TimeSpan.FromMilliseconds(10), TimeSpan.FromSeconds(1)))); + } + + [Test] + public void TestExpiryValidation() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + Assert.Throws(() => + new MongoDistributedLock("test", database, options => options.Expiry(TimeSpan.FromMilliseconds(50)))); + Assert.Throws(() => + new MongoDistributedLock("test", database, options => options.Expiry(Timeout.InfiniteTimeSpan))); + Assert.DoesNotThrow(() => + new MongoDistributedLock("test", database, options => options.Expiry(TimeSpan.FromSeconds(1)))); + } + + [Test] + public void TestExtensionCadenceValidation() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + Assert.Throws(() => + new MongoDistributedLock("test", database, options => options + .Expiry(TimeSpan.FromSeconds(5)) + .ExtensionCadence(TimeSpan.FromSeconds(10)))); + Assert.DoesNotThrow(() => + new MongoDistributedLock("test", database, options => options + .Expiry(TimeSpan.FromSeconds(10)) + .ExtensionCadence(TimeSpan.FromSeconds(3)))); + } + + [Test] + public async Task TestOptionsAreApplied() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + var lockName = TestHelper.UniqueName; + var @lock = new MongoDistributedLock(lockName, database, options => options + .Expiry(TimeSpan.FromSeconds(60)) + .ExtensionCadence(TimeSpan.FromSeconds(20)) + .BusyWaitSleepTime(TimeSpan.FromMilliseconds(5), TimeSpan.FromMilliseconds(100))); + await using (var handle = await @lock.AcquireAsync()) + { + Assert.That(handle, Is.Not.Null); + } + } +} \ No newline at end of file diff --git a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationProviderTest.cs b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationProviderTest.cs new file mode 100644 index 00000000..54879238 --- /dev/null +++ b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationProviderTest.cs @@ -0,0 +1,71 @@ +using Medallion.Threading.MongoDB; +using MongoDB.Driver; +using NUnit.Framework; + +namespace Medallion.Threading.Tests.MongoDB; + +[Category("CI")] +public class MongoDistributedSynchronizationProviderTest +{ + [Test] + public void TestArgumentValidation() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + Assert.Throws(() => new MongoDistributedSynchronizationProvider(null!)); + Assert.Throws(() => new MongoDistributedSynchronizationProvider(database, (string)null!)); + Assert.DoesNotThrow(() => new MongoDistributedSynchronizationProvider(database)); + Assert.DoesNotThrow(() => new MongoDistributedSynchronizationProvider(database, "CustomCollection")); + } + + [Test] + public void TestIDistributedLockProviderInterface() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + IDistributedLockProvider provider = new MongoDistributedSynchronizationProvider(database); + var @lock = provider.CreateLock("interfaceTest"); + Assert.That(@lock, Is.Not.Null); + Assert.That(@lock, Is.InstanceOf()); + Assert.That(@lock.Name, Is.EqualTo("interfaceTest")); + } + + [Test] + public async Task TestProviderCreateLock() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + var provider = new MongoDistributedSynchronizationProvider(database); + var lock1 = provider.CreateLock("testLock1"); + var lock2 = provider.CreateLock("testLock2"); + Assert.That(lock1, Is.Not.Null); + Assert.That(lock2, Is.Not.Null); + Assert.That(lock1.Name, Is.EqualTo("testLock1")); + Assert.That(lock2.Name, Is.EqualTo("testLock2")); + + // Test that locks work + await using (var handle1 = await lock1.AcquireAsync()) + await using (var handle2 = await lock2.AcquireAsync()) + { + Assert.That(handle1, Is.Not.Null); + Assert.That(handle2, Is.Not.Null); + } + } + + [Test] + public async Task TestProviderWithCustomCollection() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + const string CustomCollection = "TestProviderLocks"; + var provider = new MongoDistributedSynchronizationProvider(database, CustomCollection); + var @lock = provider.CreateLock("testLock"); + await using (var handle = await @lock.AcquireAsync()) + { + Assert.That(handle, Is.Not.Null); + } + + // Verify the custom collection was used + var collectionExists = (await database.ListCollectionNamesAsync()).ToList().Contains(CustomCollection); + Assert.That(collectionExists, Is.True); + + // Cleanup + await database.DropCollectionAsync(CustomCollection); + } +} \ No newline at end of file diff --git a/src/DistributedLock.Tests/packages.lock.json b/src/DistributedLock.Tests/packages.lock.json index 89913e0e..6cc0a2f3 100644 --- a/src/DistributedLock.Tests/packages.lock.json +++ b/src/DistributedLock.Tests/packages.lock.json @@ -1,593 +1,6 @@ { "version": 2, "dependencies": { - ".NETFramework,Version=v4.7.2": { - "MedallionShell.StrongName": { - "type": "Direct", - "requested": "[1.6.2, )", - "resolved": "1.6.2", - "contentHash": "x7kIh8HiLHQrm5tcLEwNXhYfIHjQoK8ZS9MPx/LcCgNubtfFVJZm8Kk5/FSOalHjlXizcLAm6733L691l8cr/Q==" - }, - "Microsoft.NET.Test.Sdk": { - "type": "Direct", - "requested": "[17.9.0, )", - "resolved": "17.9.0", - "contentHash": "7GUNAUbJYn644jzwLm5BD3a2p9C1dmP8Hr6fDPDxgItQk9hBs1Svdxzz07KQ/UphMSmgza9AbijBJGmw5D658A==", - "dependencies": { - "Microsoft.CodeCoverage": "17.9.0" - } - }, - "Moq": { - "type": "Direct", - "requested": "[4.20.70, )", - "resolved": "4.20.70", - "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", - "dependencies": { - "Castle.Core": "5.1.1", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "NUnit": { - "type": "Direct", - "requested": "[3.14.0, )", - "resolved": "3.14.0", - "contentHash": "R7iPwD7kbOaP3o2zldWJbWeMQAvDKD0uld27QvA3PAALl1unl7x0v2J7eGiJOYjimV/BuGT4VJmr45RjS7z4LA==" - }, - "NUnit.Analyzers": { - "type": "Direct", - "requested": "[4.1.0, )", - "resolved": "4.1.0", - "contentHash": "Odd1RusSMnfswIiCPbokAqmlcCCXjQ20poaXWrw+CWDnBY1vQ/x6ZGqgyJXpebPq5Uf8uEBe5iOAySsCdSrWdQ==" - }, - "NUnit3TestAdapter": { - "type": "Direct", - "requested": "[4.5.0, )", - "resolved": "4.5.0", - "contentHash": "s8JpqTe9bI2f49Pfr3dFRfoVSuFQyraTj68c3XXjIS/MRGvvkLnrg6RLqnTjdShX+AdFUCCU/4Xex58AdUfs6A==" - }, - "System.Data.SqlClient": { - "type": "Direct", - "requested": "[4.8.6, )", - "resolved": "4.8.6", - "contentHash": "2Ij/LCaTQRyAi5lAv7UUTV9R2FobC8xN9mE0fXBZohum/xLl8IZVmE98Rq5ugQHjCgTBRKqpXRb4ORulRdA6Ig==" - }, - "Azure.Core": { - "type": "Transitive", - "resolved": "1.38.0", - "contentHash": "IuEgCoVA0ef7E4pQtpC3+TkPbzaoQfa77HlfJDmfuaJUCVJmn7fT0izamZiryW5sYUFKizsftIxMkXKbgIcPMQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "1.1.1", - "System.ClientModel": "1.0.0", - "System.Diagnostics.DiagnosticSource": "6.0.1", - "System.Memory.Data": "1.0.2", - "System.Numerics.Vectors": "4.5.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Azure.Identity": { - "type": "Transitive", - "resolved": "1.11.4", - "contentHash": "Sf4BoE6Q3jTgFkgBkx7qztYOFELBCo+wQgpYDwal/qJ1unBH73ywPztIJKXBXORRzAeNijsuxhk94h0TIMvfYg==", - "dependencies": { - "Azure.Core": "1.38.0", - "Microsoft.Identity.Client": "4.61.3", - "Microsoft.Identity.Client.Extensions.Msal": "4.61.3", - "System.Memory": "4.5.4", - "System.Security.Cryptography.ProtectedData": "4.7.0", - "System.Text.Json": "4.7.2", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Azure.Storage.Common": { - "type": "Transitive", - "resolved": "12.18.1", - "contentHash": "ohCslqP9yDKIn+DVjBEOBuieB1QwsUCz+BwHYNaJ3lcIsTSiI4Evnq81HcKe8CqM8qvdModbipVQKpnxpbdWqA==", - "dependencies": { - "Azure.Core": "1.36.0", - "System.IO.Hashing": "6.0.0" - } - }, - "Castle.Core": { - "type": "Transitive", - "resolved": "5.1.1", - "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==" - }, - "Microsoft.Bcl.AsyncInterfaces": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Bcl.HashCode": { - "type": "Transitive", - "resolved": "1.1.1", - "contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA==" - }, - "Microsoft.CodeCoverage": { - "type": "Transitive", - "resolved": "17.9.0", - "contentHash": "RGD37ZSrratfScYXm7M0HjvxMxZyWZL4jm+XgMZbkIY1UPgjUpbNA/t+WTGj/rC/0Hm9A3IrH3ywbKZkOCnoZA==" - }, - "Microsoft.Data.SqlClient.SNI": { - "type": "Transitive", - "resolved": "5.2.0", - "contentHash": "0p2KMVc8WSC5JWgO+OdhYJiRM41dp6w2Dsd9JfEiHLPc6nyxBQgSrx9TYlbC8fRT2RK+HyWzDlv9ofFtxMOwQg==" - }, - "Microsoft.Extensions.DependencyInjection.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Microsoft.Extensions.Logging.Abstractions": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5" - } - }, - "Microsoft.Identity.Client": { - "type": "Transitive", - "resolved": "4.61.3", - "contentHash": "naJo/Qm35Caaoxp5utcw+R8eU8ZtLz2ALh8S+gkekOYQ1oazfCQMWVT4NJ/FnHzdIJlm8dMz0oMpMGCabx5odA==", - "dependencies": { - "Microsoft.IdentityModel.Abstractions": "6.35.0", - "System.Diagnostics.DiagnosticSource": "6.0.1" - } - }, - "Microsoft.Identity.Client.Extensions.Msal": { - "type": "Transitive", - "resolved": "4.61.3", - "contentHash": "PWnJcznrSGr25MN8ajlc2XIDW4zCFu0U6FkpaNLEWLgd1NgFCp5uDY3mqLDgM8zCN8hqj8yo5wHYfLB2HjcdGw==", - "dependencies": { - "Microsoft.Identity.Client": "4.61.3", - "System.IO.FileSystem.AccessControl": "5.0.0", - "System.Security.Cryptography.ProtectedData": "4.5.0" - } - }, - "Microsoft.IdentityModel.Abstractions": { - "type": "Transitive", - "resolved": "6.35.0", - "contentHash": "xuR8E4Rd96M41CnUSCiOJ2DBh+z+zQSmyrYHdYhD6K4fXBcQGVnRCFQ0efROUYpP+p0zC1BLKr0JRpVuujTZSg==" - }, - "Microsoft.IdentityModel.JsonWebTokens": { - "type": "Transitive", - "resolved": "6.35.0", - "contentHash": "9wxai3hKgZUb4/NjdRKfQd0QJvtXKDlvmGMYACbEC8DFaicMFCFhQFZq9ZET1kJLwZahf2lfY5Gtcpsx8zYzbg==", - "dependencies": { - "Microsoft.IdentityModel.Tokens": "6.35.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2" - } - }, - "Microsoft.IdentityModel.Logging": { - "type": "Transitive", - "resolved": "6.35.0", - "contentHash": "jePrSfGAmqT81JDCNSY+fxVWoGuJKt9e6eJ+vT7+quVS55nWl//jGjUQn4eFtVKt4rt5dXaleZdHRB9J9AJZ7Q==", - "dependencies": { - "Microsoft.IdentityModel.Abstractions": "6.35.0" - } - }, - "Microsoft.IdentityModel.Protocols": { - "type": "Transitive", - "resolved": "6.35.0", - "contentHash": "BPQhlDzdFvv1PzaUxNSk+VEPwezlDEVADIKmyxubw7IiELK18uJ06RQ9QKKkds30XI+gDu9n8j24XQ8w7fjWcg==", - "dependencies": { - "Microsoft.IdentityModel.Logging": "6.35.0", - "Microsoft.IdentityModel.Tokens": "6.35.0" - } - }, - "Microsoft.IdentityModel.Protocols.OpenIdConnect": { - "type": "Transitive", - "resolved": "6.35.0", - "contentHash": "LMtVqnECCCdSmyFoCOxIE5tXQqkOLrvGrL7OxHg41DIm1bpWtaCdGyVcTAfOQpJXvzND9zUKIN/lhngPkYR8vg==", - "dependencies": { - "Microsoft.IdentityModel.Protocols": "6.35.0", - "System.IdentityModel.Tokens.Jwt": "6.35.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2" - } - }, - "Microsoft.IdentityModel.Tokens": { - "type": "Transitive", - "resolved": "6.35.0", - "contentHash": "RN7lvp7s3Boucg1NaNAbqDbxtlLj5Qeb+4uSS1TeK5FSBVM40P4DKaTKChT43sHyKfh7V0zkrMph6DdHvyA4bg==", - "dependencies": { - "Microsoft.IdentityModel.Logging": "6.35.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.7.2" - } - }, - "Pipelines.Sockets.Unofficial": { - "type": "Transitive", - "resolved": "2.2.8", - "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ==", - "dependencies": { - "System.IO.Pipelines": "5.0.1" - } - }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.ClientModel": { - "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "I3CVkvxeqFYjIVEP59DnjbeoGNfo/+SZrCLpRz2v/g0gpCHaEMPtWSY0s9k/7jR1rAsLNg2z2u1JRB76tPjnIw==", - "dependencies": { - "System.Memory.Data": "1.0.2", - "System.Text.Json": "4.7.2" - } - }, - "System.Collections.Immutable": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Configuration.ConfigurationManager": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "jXw9MlUu/kRfEU0WyTptAVueupqIeE3/rl0EZDMlf8pcvJnitQ8HeVEp69rZdaStXwTV72boi/Bhw8lOeO+U2w==", - "dependencies": { - "System.Security.Permissions": "6.0.0" - } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Formats.Asn1": { - "type": "Transitive", - "resolved": "8.0.1", - "contentHash": "XqKba7Mm/koKSjKMfW82olQdmfbI5yqeoLV/tidRp7fbh5rmHAQ5raDI/7SU0swTzv+jgqtUGkzmFxuUg0it1A==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5", - "System.ValueTuple": "4.5.0" - } - }, - "System.IdentityModel.Tokens.Jwt": { - "type": "Transitive", - "resolved": "6.35.0", - "contentHash": "yxGIQd3BFK7F6S62/7RdZk3C/mfwyVxvh6ngd1VYMBmbJ1YZZA9+Ku6suylVtso0FjI0wbElpJ0d27CdsyLpBQ==", - "dependencies": { - "Microsoft.IdentityModel.JsonWebTokens": "6.35.0", - "Microsoft.IdentityModel.Tokens": "6.35.0" - } - }, - "System.IO.Compression": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==" - }, - "System.IO.FileSystem.AccessControl": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "SxHB3nuNrpptVk+vZ/F+7OHEpoHUIKKMl02bUmYHQr1r+glbZQxs7pRtsf4ENO29TVm2TH3AEeep2fJcy92oYw==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.IO.Hashing": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Rfm2jYCaUeGysFEZjDe7j1R4x6Z6BzumS/vUT5a1AA/AWJuGX71PoGB0RmpyX3VmrGqVnAwtfMn39OHR8Y/5+g==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4" - } - }, - "System.IO.Pipelines": { - "type": "Transitive", - "resolved": "5.0.1", - "contentHash": "qEePWsaq9LoEEIqhbGe6D5J8c9IqQOUuTzzV6wn1POlfdLkJliZY3OlB0j0f17uMWlqZYjH7txj+2YbyrIA8Yg==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.Memory.Data": { - "type": "Transitive", - "resolved": "1.0.2", - "contentHash": "JGkzeqgBsiZwKJZ1IxPNsDFZDhUvuEdX8L8BDC8N3KOj+6zMcNU28CNN59TpZE/VJYy9cP+5M+sbxtWJx3/xtw==", - "dependencies": { - "System.Text.Encodings.Web": "4.7.2", - "System.Text.Json": "4.6.0" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" - }, - "System.Runtime.CompilerServices.Unsafe": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" - }, - "System.Runtime.InteropServices.RuntimeInformation": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==" - }, - "System.Security.AccessControl": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==", - "dependencies": { - "System.Security.Principal.Windows": "5.0.0" - } - }, - "System.Security.Cryptography.ProtectedData": { - "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "ehYW0m9ptxpGWvE4zgqongBVWpSDU/JCFD4K7krxkQwSz/sFQjEXCUqpvencjy6DYDbn7Ig09R8GFffu8TtneQ==" - }, - "System.Security.Permissions": { - "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==", - "dependencies": { - "System.Security.AccessControl": "6.0.0" - } - }, - "System.Security.Principal.Windows": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" - }, - "System.Text.Encoding": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==" - }, - "System.Text.Encodings.Web": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, - "System.Text.Json": { - "type": "Transitive", - "resolved": "8.0.5", - "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4", - "System.ValueTuple": "4.5.0" - } - }, - "System.Threading.Channels": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==", - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "System.Threading.Tasks.Extensions": { - "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.ValueTuple": { - "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" - }, - "distributedlock": { - "type": "Project", - "dependencies": { - "DistributedLock.Azure": "[1.0.2, )", - "DistributedLock.FileSystem": "[1.0.3, )", - "DistributedLock.MySql": "[1.0.2, )", - "DistributedLock.Oracle": "[1.0.4, )", - "DistributedLock.Postgres": "[1.3.0, )", - "DistributedLock.Redis": "[1.1.0, )", - "DistributedLock.SqlServer": "[1.0.6, )", - "DistributedLock.WaitHandles": "[1.0.1, )", - "DistributedLock.ZooKeeper": "[1.0.0, )" - } - }, - "distributedlock.azure": { - "type": "Project", - "dependencies": { - "Azure.Storage.Blobs": "[12.19.1, )", - "DistributedLock.Core": "[1.0.8, )" - } - }, - "distributedlock.core": { - "type": "Project", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "[8.0.0, )", - "System.ValueTuple": "[4.5.0, )" - } - }, - "distributedlock.filesystem": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )" - } - }, - "distributedlock.mysql": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )", - "MySqlConnector": "[2.3.5, )" - } - }, - "distributedlock.oracle": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )", - "Oracle.ManagedDataAccess": "[23.6.1, )" - } - }, - "distributedlock.postgres": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )", - "Npgsql": "[8.0.6, )" - } - }, - "distributedlock.redis": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )", - "StackExchange.Redis": "[2.7.33, )" - } - }, - "distributedlock.sqlserver": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )", - "Microsoft.Data.SqlClient": "[5.2.2, )" - } - }, - "distributedlock.waithandles": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )" - } - }, - "distributedlock.zookeeper": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )", - "ZooKeeperNetEx": "[3.4.12.4, )" - } - }, - "Azure.Storage.Blobs": { - "type": "CentralTransitive", - "requested": "[12.19.1, )", - "resolved": "12.19.1", - "contentHash": "x43hWFJ4sPQ23TD4piCwT+KlQpZT8pNDAzqj6yUCqh+WJ2qcQa17e1gh6ZOeT2QNFQTTDSuR56fm2bIV7i11/w==", - "dependencies": { - "Azure.Storage.Common": "12.18.1", - "System.Text.Json": "4.7.2" - } - }, - "Microsoft.Data.SqlClient": { - "type": "CentralTransitive", - "requested": "[5.2.2, )", - "resolved": "5.2.2", - "contentHash": "mtoeRMh7F/OA536c/Cnh8L4H0uLSKB5kSmoi54oN7Fp0hNJDy22IqyMhaMH4PkDCqI7xL//Fvg9ldtuPHG0h5g==", - "dependencies": { - "Azure.Identity": "1.11.4", - "Microsoft.Data.SqlClient.SNI": "5.2.0", - "Microsoft.Identity.Client": "4.61.3", - "Microsoft.IdentityModel.JsonWebTokens": "6.35.0", - "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.35.0", - "System.Buffers": "4.5.1", - "System.Configuration.ConfigurationManager": "6.0.1", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Text.Encodings.Web": "6.0.0" - } - }, - "MySqlConnector": { - "type": "CentralTransitive", - "requested": "[2.3.5, )", - "resolved": "2.3.5", - "contentHash": "AmEfUPkFl+Ev6jJ8Dhns3CYHBfD12RHzGYWuLt6DfG6/af6YvOMyPz74ZPPjBYQGRJkumD2Z48Kqm8s5DJuhLA==", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "7.0.1", - "System.Diagnostics.DiagnosticSource": "7.0.2", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "Npgsql": { - "type": "CentralTransitive", - "requested": "[8.0.6, )", - "resolved": "8.0.6", - "contentHash": "KaS6CY5kY2Sd0P00MSeFcOI3t2DiQ4UWG8AuRpVOUeDWITOKfoEEG91DP3cmT6aerixPkjwKgXxnpDxIkDpO6g==", - "dependencies": { - "Microsoft.Bcl.HashCode": "1.1.1", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "System.Collections.Immutable": "8.0.0", - "System.Diagnostics.DiagnosticSource": "8.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Json": "8.0.5", - "System.Threading.Channels": "8.0.0" - } - }, - "Oracle.ManagedDataAccess": { - "type": "CentralTransitive", - "requested": "[23.6.1, )", - "resolved": "23.6.1", - "contentHash": "EZi+mahzUwQFWs9Is8ed94eTzWOlfCLMd+DDWukf/h/brTz1wB9Qk3fsxBrjw9+fEXrxDgx4uXNiPHNPRS3BeQ==", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "6.0.1", - "System.Formats.Asn1": "8.0.1", - "System.Text.Json": "8.0.5", - "System.Threading.Tasks.Extensions": "4.5.4" - } - }, - "StackExchange.Redis": { - "type": "CentralTransitive", - "requested": "[2.7.33, )", - "resolved": "2.7.33", - "contentHash": "2kCX5fvhEE824a4Ab5Imyi8DRuGuTxyklXV01kegkRpsWJcPmO6+GAQ+HegKxvXAxlXZ8yaRspvWJ8t3mMClfQ==", - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "5.0.0", - "Microsoft.Extensions.Logging.Abstractions": "6.0.0", - "Pipelines.Sockets.Unofficial": "2.2.8", - "System.IO.Compression": "4.3.0", - "System.Threading.Channels": "5.0.0" - } - }, - "ZooKeeperNetEx": { - "type": "CentralTransitive", - "requested": "[3.4.12.4, )", - "resolved": "3.4.12.4", - "contentHash": "YECtByVSH7TRjQKplwOWiKyanCqYE5eEkGk5YtHJgsnbZ6+p1o0Gvs5RIsZLotiAVa6Niez1BJyKY/RDY/L6zg==" - } - }, "net8.0": { "MedallionShell.StrongName": { "type": "Direct", @@ -692,6 +105,14 @@ "System.Diagnostics.EventLog": "6.0.0" } }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "1.1.1", @@ -797,8 +218,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "3.1.0", - "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w==" + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", @@ -829,11 +250,20 @@ }, "Microsoft.Win32.Registry": { "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.0", + "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", "dependencies": { - "System.Security.AccessControl": "4.7.0", - "System.Security.Principal.Windows": "4.7.0" + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, "NETStandard.Library": { @@ -893,6 +323,21 @@ "resolved": "4.4.0", "contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA==" }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, "System.ClientModel": { "type": "Transitive", "resolved": "1.0.0", @@ -963,8 +408,8 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Memory.Data": { "type": "Transitive", @@ -1009,11 +454,11 @@ }, "System.Security.AccessControl": { "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", "dependencies": { - "Microsoft.NETCore.Platforms": "3.1.0", - "System.Security.Principal.Windows": "4.7.0" + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" } }, "System.Security.Cryptography.Cng": { @@ -1036,8 +481,8 @@ }, "System.Security.Principal.Windows": { "type": "Transitive", - "resolved": "4.7.0", - "contentHash": "ojD0PX0XhneCsUbAZVKdb7h/70vyYMDYs85lwEI+LngEONe/17A0cFaRFqZU+sOEidcVswYWikYOQ9PPfjlbtQ==" + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" }, "System.Text.Encoding": { "type": "Transitive", @@ -1064,6 +509,11 @@ "resolved": "4.5.4", "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, "distributedlock": { "type": "Project", "dependencies": { @@ -1094,6 +544,13 @@ "DistributedLock.Core": "[1.0.8, )" } }, + "distributedlock.mongodb": { + "type": "Project", + "dependencies": { + "DistributedLock.Core": "[1.0.8, )", + "MongoDB.Driver": "[3.5.0, )" + } + }, "distributedlock.mysql": { "type": "Project", "dependencies": { @@ -1169,6 +626,21 @@ "System.Runtime.Caching": "8.0.0" } }, + "MongoDB.Driver": { + "type": "CentralTransitive", + "requested": "[3.5.0, )", + "resolved": "3.5.0", + "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, "MySqlConnector": { "type": "CentralTransitive", "requested": "[2.3.5, )", diff --git a/src/DistributedLock.sln b/src/DistributedLock.sln index 61d17d38..30c8dd91 100644 --- a/src/DistributedLock.sln +++ b/src/DistributedLock.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.9.34616.47 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11116.177 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DistributedLock", "DistributedLock\DistributedLock.csproj", "{C1F56B68-C2EE-48E5-A99B-B40D397AE34F}" EndProject @@ -47,6 +47,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution package.readme.md = package.readme.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DistributedLock.MongoDB", "DistributedLock.MongoDB\DistributedLock.MongoDB.csproj", "{92074E6D-99D1-46B1-A0AE-442EA1FEA397}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -109,6 +111,10 @@ Global {1CAB9A1D-0C02-459C-A90E-47819832BD58}.Debug|Any CPU.Build.0 = Debug|Any CPU {1CAB9A1D-0C02-459C-A90E-47819832BD58}.Release|Any CPU.ActiveCfg = Release|Any CPU {1CAB9A1D-0C02-459C-A90E-47819832BD58}.Release|Any CPU.Build.0 = Release|Any CPU + {92074E6D-99D1-46B1-A0AE-442EA1FEA397}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {92074E6D-99D1-46B1-A0AE-442EA1FEA397}.Debug|Any CPU.Build.0 = Debug|Any CPU + {92074E6D-99D1-46B1-A0AE-442EA1FEA397}.Release|Any CPU.ActiveCfg = Release|Any CPU + {92074E6D-99D1-46B1-A0AE-442EA1FEA397}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/DistributedLock/DistributedLock.csproj b/src/DistributedLock/DistributedLock.csproj index cdb3a088..48c5da8d 100644 --- a/src/DistributedLock/DistributedLock.csproj +++ b/src/DistributedLock/DistributedLock.csproj @@ -57,7 +57,7 @@ - + \ No newline at end of file diff --git a/src/DistributedLock/packages.lock.json b/src/DistributedLock/packages.lock.json index 44b4b439..5407bb0c 100644 --- a/src/DistributedLock/packages.lock.json +++ b/src/DistributedLock/packages.lock.json @@ -436,6 +436,13 @@ "DistributedLock.Core": "[1.0.8, )" } }, + "distributedlock.mongodb": { + "type": "Project", + "dependencies": { + "DistributedLock.Core": "[1.0.8, )", + "MySqlConnector": "[3.5.0, )" + } + }, "distributedlock.mysql": { "type": "Project", "dependencies": { @@ -973,6 +980,9 @@ "DistributedLock.Core": "[1.0.8, )" } }, + "distributedlock.mongodb": { + "type": "Project" + }, "distributedlock.mysql": { "type": "Project", "dependencies": { @@ -1616,6 +1626,9 @@ "DistributedLock.Core": "[1.0.8, )" } }, + "distributedlock.mongodb": { + "type": "Project" + }, "distributedlock.mysql": { "type": "Project", "dependencies": { @@ -1812,6 +1825,14 @@ "System.IO.Hashing": "6.0.0" } }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "8.0.0", @@ -1955,6 +1976,15 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.0", + "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", "resolved": "2.2.8", @@ -1963,6 +1993,22 @@ "System.IO.Pipelines": "5.0.1" } }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==", + "dependencies": { + "System.Text.Encoding.CodePages": "5.0.0" + } + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.7.1" + } + }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", @@ -2263,6 +2309,14 @@ "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "distributedlock.azure": { "type": "Project", "dependencies": { @@ -2279,6 +2333,13 @@ "DistributedLock.Core": "[1.0.8, )" } }, + "distributedlock.mongodb": { + "type": "Project", + "dependencies": { + "DistributedLock.Core": "[1.0.8, )", + "MongoDB.Driver": "[3.5.0, )" + } + }, "distributedlock.mysql": { "type": "Project", "dependencies": { @@ -2361,6 +2422,21 @@ "System.Text.Encodings.Web": "6.0.0" } }, + "MongoDB.Driver": { + "type": "CentralTransitive", + "requested": "[3.5.0, )", + "resolved": "3.5.0", + "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, "MySqlConnector": { "type": "CentralTransitive", "requested": "[2.3.5, )", diff --git a/src/DistributedLockTaker/packages.lock.json b/src/DistributedLockTaker/packages.lock.json index 1584b3e4..8476aab4 100644 --- a/src/DistributedLockTaker/packages.lock.json +++ b/src/DistributedLockTaker/packages.lock.json @@ -529,7 +529,7 @@ "contentHash": "YECtByVSH7TRjQKplwOWiKyanCqYE5eEkGk5YtHJgsnbZ6+p1o0Gvs5RIsZLotiAVa6Niez1BJyKY/RDY/L6zg==" } }, - ".NETFramework,Version=v4.7.2/win7-x86": { + ".NETFramework,Version=v4.7.2/win-x86": { "System.Configuration.ConfigurationManager": { "type": "Transitive", "resolved": "6.0.1", @@ -604,9 +604,9 @@ "net8.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.4, )", - "resolved": "8.0.4", - "contentHash": "PZb5nfQ+U19nhnmnR9T1jw+LTmozhuG2eeuzuW5A7DqxD/UXW2ucjmNJqnqOuh8rdPzM3MQXoF8AfFCedJdCUw==" + "requested": "[8.0.21, )", + "resolved": "8.0.21", + "contentHash": "s8H5PZQs50OcNkaB6Si54+v3GWM7vzs6vxFRMlD3aXsbM+aPCtod62gmK0BYWou9diGzmo56j8cIf/PziijDqQ==" }, "Azure.Core": { "type": "Transitive", @@ -1076,7 +1076,7 @@ "contentHash": "YECtByVSH7TRjQKplwOWiKyanCqYE5eEkGk5YtHJgsnbZ6+p1o0Gvs5RIsZLotiAVa6Niez1BJyKY/RDY/L6zg==" } }, - "net8.0/win7-x86": { + "net8.0/win-x86": { "Microsoft.Data.SqlClient.SNI.runtime": { "type": "Transitive", "resolved": "5.2.0", @@ -1105,11 +1105,6 @@ "resolved": "4.3.0", "contentHash": "+ihI5VaXFCMVPJNstG4O4eo1CfbrByLxRrQQTqOTp1ttK0kUKDqOdBSTaCB2IBk/QtjDrs6+x4xuezyMXdm0HQ==" }, - "runtime.win7.System.Private.Uri": { - "type": "Transitive", - "resolved": "4.3.0", - "contentHash": "Q+IBgaPYicSQs2tBlmXqbS25c/JLIthWrgrpMwxKSOobW/OqIMVFruUGfuaz4QABVzV8iKdCAbN7APY7Tclbnw==" - }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "8.0.0", @@ -1193,8 +1188,7 @@ "contentHash": "o1+7RJnu3Ik3PazR7Z7tJhjPdE000Eq2KGLLWhqJJKXj04wrS8lwb1OFtDF9jzXXADhUuZNJZlPc98uwwqmpFA==", "dependencies": { "Microsoft.NETCore.Platforms": "1.1.1", - "Microsoft.NETCore.Targets": "1.1.3", - "runtime.win7.System.Private.Uri": "4.3.0" + "Microsoft.NETCore.Targets": "1.1.3" } }, "System.Threading.AccessControl": { From 80ff241f8dc081a8702d481e1c360158aa6c3e66 Mon Sep 17 00:00:00 2001 From: Joes Date: Tue, 21 Oct 2025 18:02:53 +0800 Subject: [PATCH 02/21] =?UTF-8?q?=E6=8F=90=E9=AB=98=E5=B0=81=E8=A3=85?= =?UTF-8?q?=E6=80=A7=E5=B9=B6=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E5=8F=AF?= =?UTF-8?q?=E8=AF=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将字段和构造函数的访问修饰符从 internal 修改为 private,以增强封装性。修复注释中的拼写错误并优化格式,提升代码的可读性和维护性。 --- .../MongoDistributedSynchronizationOptionsBuilder.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs b/src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs index eae699e1..b021030d 100644 --- a/src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs +++ b/src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs @@ -7,22 +7,22 @@ namespace Medallion.Threading.MongoDB; /// public sealed class MongoDistributedSynchronizationOptionsBuilder { - internal static readonly TimeoutValue DefaultExpiry = TimeSpan.FromSeconds(30); + private static readonly TimeoutValue DefaultExpiry = TimeSpan.FromSeconds(30); /// /// We don't want to allow expiry to go too low, since then the lock doesn't even work /// - internal static readonly TimeoutValue MinimumExpiry = TimeSpan.FromSeconds(.1); + private static readonly TimeoutValue MinimumExpiry = TimeSpan.FromSeconds(.1); private TimeoutValue? _expiry, _extensionCadence, _minBusyWaitSleepTime, _maxBusyWaitSleepTime; - internal MongoDistributedSynchronizationOptionsBuilder() { } + private MongoDistributedSynchronizationOptionsBuilder() { } /// /// Specifies how long the lock will last, absent auto-extension. Because auto-extension exists, /// this value generally will have little effect on program behavior. However, making the expiry longer means that /// auto-extension requests can occur less frequently, saving resources. On the other hand, when a lock is abandoned - /// without explicit release (e. g. if the holding process crashes), the expiry determines how long other processes + /// without explicit release (e.g. if the holding process crashes), the expiry determines how long other processes /// would need to wait in order to acquire it. /// Defaults to 30s. /// From e06c413145391bd2db6c08c917ad6fc2085ec887 Mon Sep 17 00:00:00 2001 From: Joes Date: Tue, 21 Oct 2025 18:12:21 +0800 Subject: [PATCH 03/21] =?UTF-8?q?fix:=20=E4=B8=BA=20DistributedLock.MongoD?= =?UTF-8?q?B=20=E6=B7=BB=E5=8A=A0=20InternalsVisibleTo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 `AssemblyAttributes.cs` 文件中,新增了对 `DistributedLock.MongoDB` 的 `InternalsVisibleTo` 特性声明。 此更改允许 `DistributedLock.MongoDB` 程序集访问当前程序集的内部成员。 其他现有的 `InternalsVisibleTo` 特性未受影响。 --- src/DistributedLock.Core/AssemblyAttributes.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DistributedLock.Core/AssemblyAttributes.cs b/src/DistributedLock.Core/AssemblyAttributes.cs index d2e9bff3..888ed9ef 100644 --- a/src/DistributedLock.Core/AssemblyAttributes.cs +++ b/src/DistributedLock.Core/AssemblyAttributes.cs @@ -15,4 +15,5 @@ [assembly: InternalsVisibleTo("DistributedLock.ZooKeeper, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fd3af56ccc8ed94fffe25bfd651e6a5674f8f20a76d37de800dd0f7380e04f0fde2da6fa200380b14fe398605b6f470c87e5e0a0bf39ae871f07536a4994aa7a0057c4d3bcedc8fef3eecb0c88c2024a1b3289305c2393acd9fb9f9a42d0bd7826738ce864d507575ea3a1fe1746ab19823303269f79379d767949807f494be8")] [assembly: InternalsVisibleTo("DistributedLock.MySql, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fd3af56ccc8ed94fffe25bfd651e6a5674f8f20a76d37de800dd0f7380e04f0fde2da6fa200380b14fe398605b6f470c87e5e0a0bf39ae871f07536a4994aa7a0057c4d3bcedc8fef3eecb0c88c2024a1b3289305c2393acd9fb9f9a42d0bd7826738ce864d507575ea3a1fe1746ab19823303269f79379d767949807f494be8")] [assembly: InternalsVisibleTo("DistributedLock.Oracle, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fd3af56ccc8ed94fffe25bfd651e6a5674f8f20a76d37de800dd0f7380e04f0fde2da6fa200380b14fe398605b6f470c87e5e0a0bf39ae871f07536a4994aa7a0057c4d3bcedc8fef3eecb0c88c2024a1b3289305c2393acd9fb9f9a42d0bd7826738ce864d507575ea3a1fe1746ab19823303269f79379d767949807f494be8")] +[assembly: InternalsVisibleTo("DistributedLock.MongoDB, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fd3af56ccc8ed94fffe25bfd651e6a5674f8f20a76d37de800dd0f7380e04f0fde2da6fa200380b14fe398605b6f470c87e5e0a0bf39ae871f07536a4994aa7a0057c4d3bcedc8fef3eecb0c88c2024a1b3289305c2393acd9fb9f9a42d0bd7826738ce864d507575ea3a1fe1746ab19823303269f79379d767949807f494be8")] #endif From 003ce7b9f262c8c5583b931d60f9c5208f33f83b Mon Sep 17 00:00:00 2001 From: Joes Date: Wed, 22 Oct 2025 09:18:07 +0800 Subject: [PATCH 04/21] fix: ci error --- src/DistributedLock.Tests/packages.lock.json | 1 + src/DistributedLock/DistributedLock.csproj | 1 + src/DistributedLock/packages.lock.json | 144 ++++++++- src/DistributedLockTaker/packages.lock.json | 305 ++++++++++++++++++- 4 files changed, 431 insertions(+), 20 deletions(-) diff --git a/src/DistributedLock.Tests/packages.lock.json b/src/DistributedLock.Tests/packages.lock.json index 6cc0a2f3..8fcb7525 100644 --- a/src/DistributedLock.Tests/packages.lock.json +++ b/src/DistributedLock.Tests/packages.lock.json @@ -519,6 +519,7 @@ "dependencies": { "DistributedLock.Azure": "[1.0.2, )", "DistributedLock.FileSystem": "[1.0.3, )", + "DistributedLock.MongoDB": "[1.3.0, )", "DistributedLock.MySql": "[1.0.2, )", "DistributedLock.Oracle": "[1.0.4, )", "DistributedLock.Postgres": "[1.3.0, )", diff --git a/src/DistributedLock/DistributedLock.csproj b/src/DistributedLock/DistributedLock.csproj index 48c5da8d..2cbbc0e1 100644 --- a/src/DistributedLock/DistributedLock.csproj +++ b/src/DistributedLock/DistributedLock.csproj @@ -54,6 +54,7 @@ + diff --git a/src/DistributedLock/packages.lock.json b/src/DistributedLock/packages.lock.json index 5407bb0c..8d115faf 100644 --- a/src/DistributedLock/packages.lock.json +++ b/src/DistributedLock/packages.lock.json @@ -436,13 +436,6 @@ "DistributedLock.Core": "[1.0.8, )" } }, - "distributedlock.mongodb": { - "type": "Project", - "dependencies": { - "DistributedLock.Core": "[1.0.8, )", - "MySqlConnector": "[3.5.0, )" - } - }, "distributedlock.mysql": { "type": "Project", "dependencies": { @@ -628,6 +621,15 @@ "System.IO.Hashing": "6.0.0" } }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0", + "System.Buffers": "4.5.1" + } + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "8.0.0", @@ -750,6 +752,24 @@ "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.0", + "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", "resolved": "2.2.8", @@ -758,6 +778,25 @@ "System.IO.Pipelines": "5.0.1" } }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Text.Encoding.CodePages": "5.0.0" + } + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "4.7.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", @@ -817,6 +856,11 @@ "Microsoft.IdentityModel.Tokens": "6.35.0" } }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==" + }, "System.IO.Compression": { "type": "Transitive", "resolved": "4.3.0", @@ -869,11 +913,24 @@ "System.Text.Json": "4.6.0" } }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.4", + "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", + "dependencies": { + "System.Security.Cryptography.X509Certificates": "4.3.0" + } + }, "System.Numerics.Vectors": { "type": "Transitive", "resolved": "4.5.0", "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==" + }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", @@ -892,11 +949,41 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==" + }, + "System.Security.Cryptography.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==" + }, "System.Security.Cryptography.ProtectedData": { "type": "Transitive", "resolved": "4.7.0", "contentHash": "ehYW0m9ptxpGWvE4zgqongBVWpSDU/JCFD4K7krxkQwSz/sFQjEXCUqpvencjy6DYDbn7Ig09R8GFffu8TtneQ==" }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0" + } + }, "System.Security.Permissions": { "type": "Transitive", "resolved": "6.0.0", @@ -915,6 +1002,14 @@ "resolved": "4.3.0", "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==" }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "NyscU59xX6Uo91qvhOs2Ccho3AR2TnZPomo1Z0K6YpyztBPM/A5VbkzOO19sy3A3i1TtEnTxA7bCe3Us+r5MWg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "System.Text.Encodings.Web": { "type": "Transitive", "resolved": "8.0.0", @@ -960,6 +1055,15 @@ "resolved": "4.5.0", "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Memory": "4.5.5" + } + }, "distributedlock.azure": { "type": "Project", "dependencies": { @@ -981,7 +1085,11 @@ } }, "distributedlock.mongodb": { - "type": "Project" + "type": "Project", + "dependencies": { + "DistributedLock.Core": "[1.0.8, )", + "MongoDB.Driver": "[3.5.0, )" + } }, "distributedlock.mysql": { "type": "Project", @@ -1058,6 +1166,23 @@ "System.Text.Encodings.Web": "6.0.0" } }, + "MongoDB.Driver": { + "type": "CentralTransitive", + "requested": "[3.5.0, )", + "resolved": "3.5.0", + "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "System.Net.Http": "4.3.4", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "ZstdSharp.Port": "0.7.3" + } + }, "MySqlConnector": { "type": "CentralTransitive", "requested": "[2.3.5, )", @@ -1626,9 +1751,6 @@ "DistributedLock.Core": "[1.0.8, )" } }, - "distributedlock.mongodb": { - "type": "Project" - }, "distributedlock.mysql": { "type": "Project", "dependencies": { diff --git a/src/DistributedLockTaker/packages.lock.json b/src/DistributedLockTaker/packages.lock.json index 8476aab4..0feb1eeb 100644 --- a/src/DistributedLockTaker/packages.lock.json +++ b/src/DistributedLockTaker/packages.lock.json @@ -40,6 +40,15 @@ "System.IO.Hashing": "6.0.0" } }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0", + "System.Buffers": "4.5.1" + } + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "8.0.0", @@ -152,6 +161,24 @@ "System.Text.Json": "4.7.2" } }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.0", + "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "Pipelines.Sockets.Unofficial": { "type": "Transitive", "resolved": "2.2.8", @@ -160,6 +187,25 @@ "System.IO.Pipelines": "5.0.1" } }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Text.Encoding.CodePages": "5.0.0" + } + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "4.7.1", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", @@ -219,6 +265,11 @@ "Microsoft.IdentityModel.Tokens": "6.35.0" } }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==" + }, "System.IO.Compression": { "type": "Transitive", "resolved": "4.3.0", @@ -271,11 +322,24 @@ "System.Text.Json": "4.6.0" } }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.4", + "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", + "dependencies": { + "System.Security.Cryptography.X509Certificates": "4.3.0" + } + }, "System.Numerics.Vectors": { "type": "Transitive", "resolved": "4.5.0", "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==" + }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", @@ -294,11 +358,41 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==" + }, + "System.Security.Cryptography.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==" + }, "System.Security.Cryptography.ProtectedData": { "type": "Transitive", "resolved": "4.7.0", "contentHash": "ehYW0m9ptxpGWvE4zgqongBVWpSDU/JCFD4K7krxkQwSz/sFQjEXCUqpvencjy6DYDbn7Ig09R8GFffu8TtneQ==" }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0" + } + }, "System.Security.Permissions": { "type": "Transitive", "resolved": "6.0.0", @@ -317,6 +411,14 @@ "resolved": "4.3.0", "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==" }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "NyscU59xX6Uo91qvhOs2Ccho3AR2TnZPomo1Z0K6YpyztBPM/A5VbkzOO19sy3A3i1TtEnTxA7bCe3Us+r5MWg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "System.Text.Encodings.Web": { "type": "Transitive", "resolved": "8.0.0", @@ -362,11 +464,21 @@ "resolved": "4.5.0", "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "5.0.0", + "System.Memory": "4.5.5" + } + }, "distributedlock": { "type": "Project", "dependencies": { "DistributedLock.Azure": "[1.0.2, )", "DistributedLock.FileSystem": "[1.0.3, )", + "DistributedLock.MongoDB": "[1.3.0, )", "DistributedLock.MySql": "[1.0.2, )", "DistributedLock.Oracle": "[1.0.4, )", "DistributedLock.Postgres": "[1.3.0, )", @@ -396,6 +508,13 @@ "DistributedLock.Core": "[1.0.8, )" } }, + "distributedlock.mongodb": { + "type": "Project", + "dependencies": { + "DistributedLock.Core": "[1.0.8, )", + "MongoDB.Driver": "[3.5.0, )" + } + }, "distributedlock.mysql": { "type": "Project", "dependencies": { @@ -471,6 +590,23 @@ "System.Text.Encodings.Web": "6.0.0" } }, + "MongoDB.Driver": { + "type": "CentralTransitive", + "requested": "[3.5.0, )", + "resolved": "3.5.0", + "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "System.Net.Http": "4.3.4", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "ZstdSharp.Port": "0.7.3" + } + }, "MySqlConnector": { "type": "CentralTransitive", "requested": "[2.3.5, )", @@ -530,6 +666,15 @@ } }, ".NETFramework,Version=v4.7.2/win-x86": { + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, "System.Configuration.ConfigurationManager": { "type": "Transitive", "resolved": "6.0.1", @@ -552,6 +697,14 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.4", + "contentHash": "aOa2d51SEbmM+H+Csw7yJOuNZoHkrP2XnAurye5HWYgGVVU54YZDvsLUYRv6h18X3sPnjNCANmN7ZhIPiqMcjA==", + "dependencies": { + "System.Security.Cryptography.X509Certificates": "4.3.0" + } + }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", "resolved": "4.3.0", @@ -565,11 +718,36 @@ "System.Security.Principal.Windows": "5.0.0" } }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==" + }, "System.Security.Cryptography.ProtectedData": { "type": "Transitive", "resolved": "4.7.0", "contentHash": "ehYW0m9ptxpGWvE4zgqongBVWpSDU/JCFD4K7krxkQwSz/sFQjEXCUqpvencjy6DYDbn7Ig09R8GFffu8TtneQ==" }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0" + } + }, "System.Security.Permissions": { "type": "Transitive", "resolved": "6.0.0", @@ -583,6 +761,14 @@ "resolved": "5.0.0", "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "NyscU59xX6Uo91qvhOs2Ccho3AR2TnZPomo1Z0K6YpyztBPM/A5VbkzOO19sy3A3i1TtEnTxA7bCe3Us+r5MWg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "Microsoft.Data.SqlClient": { "type": "CentralTransitive", "requested": "[5.2.2, )", @@ -646,6 +832,14 @@ "System.IO.Hashing": "6.0.0" } }, + "DnsClient": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "1.1.1", @@ -746,8 +940,8 @@ }, "Microsoft.NETCore.Platforms": { "type": "Transitive", - "resolved": "1.1.0", - "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" }, "Microsoft.NETCore.Targets": { "type": "Transitive", @@ -759,6 +953,24 @@ "resolved": "1.0.0", "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.0", + "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } + }, "Oracle.ManagedDataAccess.Core": { "type": "Transitive", "resolved": "23.6.1", @@ -778,6 +990,21 @@ "System.IO.Pipelines": "5.0.1" } }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, "System.ClientModel": { "type": "Transitive", "resolved": "1.0.0", @@ -848,8 +1075,8 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Memory.Data": { "type": "Transitive", @@ -887,6 +1114,15 @@ "resolved": "6.0.0", "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, "System.Security.Cryptography.Cng": { "type": "Transitive", "resolved": "4.5.0", @@ -905,6 +1141,11 @@ "resolved": "8.0.0", "contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg==" }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, "System.Text.Encoding": { "type": "Transitive", "resolved": "4.3.0", @@ -930,11 +1171,17 @@ "resolved": "4.5.4", "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==" }, + "ZstdSharp.Port": { + "type": "Transitive", + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" + }, "distributedlock": { "type": "Project", "dependencies": { "DistributedLock.Azure": "[1.0.2, )", "DistributedLock.FileSystem": "[1.0.3, )", + "DistributedLock.MongoDB": "[1.3.0, )", "DistributedLock.MySql": "[1.0.2, )", "DistributedLock.Oracle": "[1.0.4, )", "DistributedLock.Postgres": "[1.3.0, )", @@ -960,6 +1207,13 @@ "DistributedLock.Core": "[1.0.8, )" } }, + "distributedlock.mongodb": { + "type": "Project", + "dependencies": { + "DistributedLock.Core": "[1.0.8, )", + "MongoDB.Driver": "[3.5.0, )" + } + }, "distributedlock.mysql": { "type": "Project", "dependencies": { @@ -1035,6 +1289,21 @@ "System.Runtime.Caching": "8.0.0" } }, + "MongoDB.Driver": { + "type": "CentralTransitive", + "requested": "[3.5.0, )", + "resolved": "3.5.0", + "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.0", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "System.Buffers": "4.5.1", + "ZstdSharp.Port": "0.7.3" + } + }, "MySqlConnector": { "type": "CentralTransitive", "requested": "[2.3.5, )", @@ -1082,16 +1351,20 @@ "resolved": "5.2.0", "contentHash": "po1jhvFd+8pbfvJR/puh+fkHi0GRanAdvayh/0e47yaM6CXWZ6opUjCMFuYlAnD2LcbyvQE7fPJKvogmaUcN+w==" }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "1.1.1", - "contentHash": "TMBuzAHpTenGbGgk0SMTwyEkyijY/Eae4ZGsFNYJvAr/LDn1ku3Etp3FPxChmDp5HHF3kzJuoaa08N0xjqAJfQ==" - }, "Microsoft.NETCore.Targets": { "type": "Transitive", "resolved": "1.1.3", "contentHash": "3Wrmi0kJDzClwAC+iBdUBpEKmEle8FQNsCs77fkiOIw/9oYA07bL1EZNX0kQ2OMN3xpwvl0vAtOCYY3ndDNlhQ==" }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, "runtime.any.System.Runtime": { "type": "Transitive", "resolved": "4.3.0", @@ -1141,6 +1414,15 @@ "System.Configuration.ConfigurationManager": "8.0.0" } }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, "System.Security.Cryptography.Cng": { "type": "Transitive", "resolved": "4.5.0", @@ -1154,6 +1436,11 @@ "System.Formats.Asn1": "8.0.0" } }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + }, "System.Text.Encoding": { "type": "Transitive", "resolved": "4.3.0", From e26e3b7013b2737e5df932a927ad91b9300b3c17 Mon Sep 17 00:00:00 2001 From: Joes Date: Wed, 22 Oct 2025 09:19:31 +0800 Subject: [PATCH 05/21] add authors --- src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj b/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj index 828c3eaa..7259e5a4 100644 --- a/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj +++ b/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj @@ -13,7 +13,7 @@ 1.3.0 1.0.0.0 - Michael Adelson + Michael Adelson, joesdu Provides a distributed lock implementation based on MongoDB Copyright © 2020 Michael Adelson MIT From b2cf5aa8425ad781951a122ad9719d82e4e49013 Mon Sep 17 00:00:00 2001 From: Joes Date: Wed, 22 Oct 2025 09:23:52 +0800 Subject: [PATCH 06/21] =?UTF-8?q?fix:=20error=20RS0025,=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=8F=AF=E7=A9=BA=E5=BC=95=E7=94=A8=E7=B1=BB=E5=9E=8B=E6=8C=87?= =?UTF-8?q?=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 `PublicAPI.Unshipped.txt` 文件中,删除了 `Medallion.Threading.MongoDB.MongoDistributedLock+#nullable enable` 行。这意味着不再启用该文件中的可空引用类型功能,可能会影响到代码的可空性检查和相关的编译器警告。 --- src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt b/src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt index 1883be4e..815c9200 100644 --- a/src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt +++ b/src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt @@ -1 +1 @@ -Medallion.Threading.MongoDB.MongoDistributedLock \ No newline at end of file +#nullable enable \ No newline at end of file From b0fe2d0b5561e955b6cdb1e70e4410db6b744e61 Mon Sep 17 00:00:00 2001 From: Joes Date: Wed, 22 Oct 2025 09:38:15 +0800 Subject: [PATCH 07/21] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=20DistributedL?= =?UTF-8?q?ock.MongoDB=20=E5=AE=8C=E6=95=B4=E6=96=87=E6=A1=A3=E5=B9=B6?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20README=20=E6=8E=92=E7=89=88=E4=B8=8E?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E7=BC=A9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 149 ++++++++++++++++---------------- docs/DistributedLock.MongoDB.md | 82 ++++++++++++++++++ 2 files changed, 158 insertions(+), 73 deletions(-) create mode 100644 docs/DistributedLock.MongoDB.md diff --git a/README.md b/README.md index 403f776b..b7e1de00 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ DistributedLock is a .NET library that provides robust and easy-to-use distributed mutexes, reader-writer locks, and semaphores based on a variety of underlying technologies. With DistributedLock, synchronizing access to a region of code across multiple applications/machines is as simple as: + ```C# await using (await myDistributedLock.AcquireAsync()) { @@ -12,12 +13,13 @@ await using (await myDistributedLock.AcquireAsync()) ## Implementations -DistributedLock contains implementations based on various technologies; you can install implementation packages individually or just install the [DistributedLock NuGet package](https://www.nuget.org/packages/DistributedLock) [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.svg?style=flat)](https://www.nuget.org/packages/DistributedLock/), a ["meta" package](https://endjin.com/blog/2020/09/streamline-dependency-management-with-nuget-meta-packages) which includes all implementations as dependencies. *Note that each package is versioned independently according to SemVer*. +DistributedLock contains implementations based on various technologies; you can install implementation packages individually or just install the [DistributedLock NuGet package](https://www.nuget.org/packages/DistributedLock) [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.svg?style=flat)](https://www.nuget.org/packages/DistributedLock/), a ["meta" package](https://endjin.com/blog/2020/09/streamline-dependency-management-with-nuget-meta-packages) which includes all implementations as dependencies. _Note that each package is versioned independently according to SemVer_. - **[DistributedLock.SqlServer](docs/DistributedLock.SqlServer.md)** [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.SqlServer.svg?style=flat)](https://www.nuget.org/packages/DistributedLock.SqlServer/) [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/distributedlock/api/Medallion.Threading.SqlServer.html) -: uses Microsoft SQL Server + : uses Microsoft SQL Server - **[DistributedLock.Postgres](docs/DistributedLock.Postgres.md)** [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.Postgres.svg?style=flat)](https://www.nuget.org/packages/DistributedLock.Postgres/) [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/distributedlock/api/Medallion.Threading.Postgres.html) -: uses Postgresql + : uses Postgresql +- **[DistributedLock.MongoDB](docs/DistributedLock.MongoDB.md)** [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.MongoDB.svg?style=flat)](https://www.nuget.org/packages/DistributedLock.MongoDB/) [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/distributedlock/api/Medallion.Threading.MongoDB.html): uses MongoDB - **[DistributedLock.MySql](docs/DistributedLock.MySql.md)** [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.MySql.svg?style=flat)](https://www.nuget.org/packages/DistributedLock.MySql/) [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/distributedlock/api/Medallion.Threading.MySql.html): uses MySQL or MariaDB - **[DistributedLock.Oracle](docs/DistributedLock.Oracle.md)** [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.Oracle.svg?style=flat)](https://www.nuget.org/packages/DistributedLock.Oracle/) [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/distributedlock/api/Medallion.Threading.Oracle.html): uses Oracle - **[DistributedLock.Redis](docs/DistributedLock.Redis.md)** [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.Redis.svg?style=flat)](https://www.nuget.org/packages/DistributedLock.Redis/) [![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/distributedlock/api/Medallion.Threading.Redis.html): uses Redis @@ -108,7 +110,7 @@ public class SomeService { this._synchronizationProvider = synchronizationProvider; } - + public void InitializeUserAccount(int id) { // use the provider to construct a lock @@ -117,7 +119,7 @@ public class SomeService { // do stuff } - + // ALTERNATIVELY, for common use-cases extension methods allow this to be done with a single call using (this._synchronizationProvider.AcquireLock($"UserAccount{id}")) { @@ -141,94 +143,95 @@ Contributions are welcome! If you are interested in contributing towards a new o Setup steps for working with the repository locally are documented [here](docs/Developing%20DistributedLock.md). ## Release notes + - 2.7 - - Add support for fetching a Redis-based semaphore's current available count. Thanks [@teesoftech](https://github.com/teesofttech) for implementing! ([#234](https://github.com/madelson/DistributedLock/issues/234), DistributedLock.Redis 1.1) + - Add support for fetching a Redis-based semaphore's current available count. Thanks [@teesoftech](https://github.com/teesofttech) for implementing! ([#234](https://github.com/madelson/DistributedLock/issues/234), DistributedLock.Redis 1.1) - 2.6 - - Add support for acquiring transaction-scoped Postgres locks using externally-owned transactions. Thanks [@Tzachi009](https://github.com/Tzachi009) for implementing! ([#213](https://github.com/madelson/DistributedLock/issues/213), DistributedLock.Postgres 1.3) + - Add support for acquiring transaction-scoped Postgres locks using externally-owned transactions. Thanks [@Tzachi009](https://github.com/Tzachi009) for implementing! ([#213](https://github.com/madelson/DistributedLock/issues/213), DistributedLock.Postgres 1.3) - 2.5.1 - - Increase efficiency of Azure blob locks when the blob does not exist. Thanks [@richardkooiman](https://github.com/richardkooiman) for implementing! ([#227](https://github.com/madelson/DistributedLock/pull/227), DistributedLock.Azure 1.0.2) - - Improve error handling in race condition scenarios for Azure blobs. Thanks [@MartinDembergerR9](https://github.com/MartinDembergerR9) for implementing! ([#228](https://github.com/madelson/DistributedLock/pull/228), DistributedLock.Azure 1.0.2) - - Bump Microsoft.Data.SqlClient to 5.2.2 to avoid vulnerability. Thanks [@steve85](https://github.com/steve85) for implementing! ([#229](https://github.com/madelson/DistributedLock/pull/229), DistributedLock.SqlServer 1.0.6) - - Bump Oracle.ManagedDataAccess to latest to avoid bringing in vulnerable packages (DistributedLock.Core 1.0.8, DistributedLock.Oracle 1.0.4) - - Bump Npgsql to latest patch to avoid bringing in vulnerable packages (DistributedLock.Postgres 1.2.1) - - Improve directory creation concurrency handling for `FileDistributedLock` (DistributedLock.FileSystem 1.0.3) + - Increase efficiency of Azure blob locks when the blob does not exist. Thanks [@richardkooiman](https://github.com/richardkooiman) for implementing! ([#227](https://github.com/madelson/DistributedLock/pull/227), DistributedLock.Azure 1.0.2) + - Improve error handling in race condition scenarios for Azure blobs. Thanks [@MartinDembergerR9](https://github.com/MartinDembergerR9) for implementing! ([#228](https://github.com/madelson/DistributedLock/pull/228), DistributedLock.Azure 1.0.2) + - Bump Microsoft.Data.SqlClient to 5.2.2 to avoid vulnerability. Thanks [@steve85](https://github.com/steve85) for implementing! ([#229](https://github.com/madelson/DistributedLock/pull/229), DistributedLock.SqlServer 1.0.6) + - Bump Oracle.ManagedDataAccess to latest to avoid bringing in vulnerable packages (DistributedLock.Core 1.0.8, DistributedLock.Oracle 1.0.4) + - Bump Npgsql to latest patch to avoid bringing in vulnerable packages (DistributedLock.Postgres 1.2.1) + - Improve directory creation concurrency handling for `FileDistributedLock` (DistributedLock.FileSystem 1.0.3) - 2.5 - - Add support for creating Postgres locks off `DbDataSource` which is helpful for apps using `NpgsqlMultiHostDataSource`. Thanks [davidngjy](https://github.com/davidngjy) for implementing! ([#153](https://github.com/madelson/DistributedLock/issues/153), DistributedLock.Postgres 1.2.0) - - Upgrade Npgsql to 8.0.3 to avoid vulnerability. Thanks [@Meir017](https://github.com/Meir017)/[@davidngjy](https://github.com/davidngjy) for implementing! ([#218](https://github.com/madelson/DistributedLock/issues/218), DistributedLock.Postgres 1.2.0) - - Fix Postgres race condition with connection keepalive enabled ([#216](https://github.com/madelson/DistributedLock/issues/216), DistributedLock.Core 1.0.7) - - Upgrade Microsoft.Data.SqlClient to 5.2.1 to avoid vulnerability ([#210](https://github.com/madelson/DistributedLock/issues/210), DistributedLock.SqlServer 1.0.5) + - Add support for creating Postgres locks off `DbDataSource` which is helpful for apps using `NpgsqlMultiHostDataSource`. Thanks [davidngjy](https://github.com/davidngjy) for implementing! ([#153](https://github.com/madelson/DistributedLock/issues/153), DistributedLock.Postgres 1.2.0) + - Upgrade Npgsql to 8.0.3 to avoid vulnerability. Thanks [@Meir017](https://github.com/Meir017)/[@davidngjy](https://github.com/davidngjy) for implementing! ([#218](https://github.com/madelson/DistributedLock/issues/218), DistributedLock.Postgres 1.2.0) + - Fix Postgres race condition with connection keepalive enabled ([#216](https://github.com/madelson/DistributedLock/issues/216), DistributedLock.Core 1.0.7) + - Upgrade Microsoft.Data.SqlClient to 5.2.1 to avoid vulnerability ([#210](https://github.com/madelson/DistributedLock/issues/210), DistributedLock.SqlServer 1.0.5) - 2.4 - - Add support for transaction-scoped locking in Postgres using `pg_advisory_xact_lock` which is helpful when using PgBouncer ([#168](https://github.com/madelson/DistributedLock/issues/168), DistributedLock.Postgres 1.1.0) - - Improve support for newer versions of StackExchange.Redis, especially when using the default backlog policy ([#162](https://github.com/madelson/DistributedLock/issues/162), DistributedLock.Redis 1.0.3). Thanks [@Bartleby2718](https://github.com/Bartleby2718) for helping with this! - - Drop `net461` support (`net462` remains supported). Thanks [@Bartleby2718](https://github.com/Bartleby2718) for implementing! - - Reduce occurrence of `UnobservedTaskException`s thrown by the library ([#192](https://github.com/madelson/DistributedLock/issues/192), DistributedLock.Core 1.0.6) - - Update dependencies to modern versions without known issues/vulnerabilities ([#111](https://github.com/madelson/DistributedLock/issues/111)/[#177](https://github.com/madelson/DistributedLock/issues/177)/[#184](https://github.com/madelson/DistributedLock/issues/184)/[#185](https://github.com/madelson/DistributedLock/issues/185), all packages). Thanks [@Bartleby2718](https://github.com/Bartleby2718) for helping with this! - - Improve directory creation concurrency handling for `FileDistributedLock` on Linux/.NET 8 ([#195](https://github.com/madelson/DistributedLock/issues/195), DistributedLock.FileSystem 1.0.2) - - Allow using transaction-scoped locks in SQL Server without explicitly disabling multiplexing ([#189](https://github.com/madelson/DistributedLock/issues/189), DistributedLock.SqlServer 1.0.4) - - New API documentation on [dndocs](https://dndocs.com/). Thanks [@NeuroXiq](https://github.com/NeuroXiq)! - - New documentation for contributors to get the project running locally (see [Contributing](#contributing)) + - Add support for transaction-scoped locking in Postgres using `pg_advisory_xact_lock` which is helpful when using PgBouncer ([#168](https://github.com/madelson/DistributedLock/issues/168), DistributedLock.Postgres 1.1.0) + - Improve support for newer versions of StackExchange.Redis, especially when using the default backlog policy ([#162](https://github.com/madelson/DistributedLock/issues/162), DistributedLock.Redis 1.0.3). Thanks [@Bartleby2718](https://github.com/Bartleby2718) for helping with this! + - Drop `net461` support (`net462` remains supported). Thanks [@Bartleby2718](https://github.com/Bartleby2718) for implementing! + - Reduce occurrence of `UnobservedTaskException`s thrown by the library ([#192](https://github.com/madelson/DistributedLock/issues/192), DistributedLock.Core 1.0.6) + - Update dependencies to modern versions without known issues/vulnerabilities ([#111](https://github.com/madelson/DistributedLock/issues/111)/[#177](https://github.com/madelson/DistributedLock/issues/177)/[#184](https://github.com/madelson/DistributedLock/issues/184)/[#185](https://github.com/madelson/DistributedLock/issues/185), all packages). Thanks [@Bartleby2718](https://github.com/Bartleby2718) for helping with this! + - Improve directory creation concurrency handling for `FileDistributedLock` on Linux/.NET 8 ([#195](https://github.com/madelson/DistributedLock/issues/195), DistributedLock.FileSystem 1.0.2) + - Allow using transaction-scoped locks in SQL Server without explicitly disabling multiplexing ([#189](https://github.com/madelson/DistributedLock/issues/189), DistributedLock.SqlServer 1.0.4) + - New API documentation on [dndocs](https://dndocs.com/). Thanks [@NeuroXiq](https://github.com/NeuroXiq)! + - New documentation for contributors to get the project running locally (see [Contributing](#contributing)) - 2.3.4 - - Support Npgsql 8.0's [ExecuteScalar breaking change](https://github.com/npgsql/npgsql/issues/5143) ([#174](https://github.com/madelson/DistributedLock/issues/174), DistributedLock.Postgres 1.0.5). Thanks [@Kaffeetasse](https://github.com/Kaffeetasse) for diagnosing and fixing! + - Support Npgsql 8.0's [ExecuteScalar breaking change](https://github.com/npgsql/npgsql/issues/5143) ([#174](https://github.com/madelson/DistributedLock/issues/174), DistributedLock.Postgres 1.0.5). Thanks [@Kaffeetasse](https://github.com/Kaffeetasse) for diagnosing and fixing! - 2.3.3 - - Update Microsoft.Data.SqlClient due to vulnerabilities ([#149](https://github.com/madelson/DistributedLock/issues/149), DistributedLock.SqlServer 1.0.3) - - Update versions of Oracle.ManagedDataAccess and Oracle.ManagedDataAccess.Core due to vulnerabilities (DistributedLock.Oracle 1.0.2) + - Update Microsoft.Data.SqlClient due to vulnerabilities ([#149](https://github.com/madelson/DistributedLock/issues/149), DistributedLock.SqlServer 1.0.3) + - Update versions of Oracle.ManagedDataAccess and Oracle.ManagedDataAccess.Core due to vulnerabilities (DistributedLock.Oracle 1.0.2) - 2.3.2 - - Work around underlying Postgres race condition when waiting on advisory locks with a short non-zero timeout ([#147](https://github.com/madelson/DistributedLock/issues/147), DistributedLock.Postgres 1.0.4). Thanks [@Tzachi009](https://github.com/Tzachi009) for reporting and isolating the issue! + - Work around underlying Postgres race condition when waiting on advisory locks with a short non-zero timeout ([#147](https://github.com/madelson/DistributedLock/issues/147), DistributedLock.Postgres 1.0.4). Thanks [@Tzachi009](https://github.com/Tzachi009) for reporting and isolating the issue! - 2.3.1 - - Fixed concurrency issue with `HandleLostToken` for relational database locks ([#133](https://github.com/madelson/DistributedLock/issues/133), DistributedLock.Core 1.0.5, DistributedLock.MySql 1.0.1, DistributedLock.Oracle 1.0.1, DistributedLock.Postgres 1.0.3, DistributedLock.SqlServer 1.0.2). Thanks [@OskarKlintrot](https://github.com/OskarKlintrot) for testing! - - Fixed misleading error message why trying to disable auto-extension in Redis ([#130](https://github.com/madelson/DistributedLock/issues/130), DistributedLock.Redis 1.0.2) - - Fixed concurrency issue with canceling async waits on `WaitHandle`s ([#120](https://github.com/madelson/DistributedLock/issues/120), DistributedLock.WaitHandles 1.0.1) + - Fixed concurrency issue with `HandleLostToken` for relational database locks ([#133](https://github.com/madelson/DistributedLock/issues/133), DistributedLock.Core 1.0.5, DistributedLock.MySql 1.0.1, DistributedLock.Oracle 1.0.1, DistributedLock.Postgres 1.0.3, DistributedLock.SqlServer 1.0.2). Thanks [@OskarKlintrot](https://github.com/OskarKlintrot) for testing! + - Fixed misleading error message why trying to disable auto-extension in Redis ([#130](https://github.com/madelson/DistributedLock/issues/130), DistributedLock.Redis 1.0.2) + - Fixed concurrency issue with canceling async waits on `WaitHandle`s ([#120](https://github.com/madelson/DistributedLock/issues/120), DistributedLock.WaitHandles 1.0.1) - 2.3.0 - - Added Oracle-based implementation ([#45](https://github.com/madelson/DistributedLock/issues/45), DistributedLock.Oracle 1.0.0). Thanks [@odin568](https://github.com/odin568) for testing! - - Made file-based locking more robust to transient `UnauthorizedAccessException`s ([#106](https://github.com/madelson/DistributedLock/issues/106) & [#109](https://github.com/madelson/DistributedLock/issues/109), DistributedLock.FileSystem 1.0.1) - - Work around cancellation bug in Npgsql command preparation ([#112](https://github.com/madelson/DistributedLock/issues/112), DistributedLock.Postgres 1.0.2) + - Added Oracle-based implementation ([#45](https://github.com/madelson/DistributedLock/issues/45), DistributedLock.Oracle 1.0.0). Thanks [@odin568](https://github.com/odin568) for testing! + - Made file-based locking more robust to transient `UnauthorizedAccessException`s ([#106](https://github.com/madelson/DistributedLock/issues/106) & [#109](https://github.com/madelson/DistributedLock/issues/109), DistributedLock.FileSystem 1.0.1) + - Work around cancellation bug in Npgsql command preparation ([#112](https://github.com/madelson/DistributedLock/issues/112), DistributedLock.Postgres 1.0.2) - 2.2.0 - - Added MySQL/MariaDB-based implementation ([#95](https://github.com/madelson/DistributedLock/issues/95), DistributedLock.MySql 1.0.0). Thanks [@theplacefordev](https://github.com/theplacefordev) for testing! + - Added MySQL/MariaDB-based implementation ([#95](https://github.com/madelson/DistributedLock/issues/95), DistributedLock.MySql 1.0.0). Thanks [@theplacefordev](https://github.com/theplacefordev) for testing! - 2.1.0 - - Added ZooKeeper-based implementation ([#41](https://github.com/madelson/DistributedLock/issues/41), DistributedLock.ZooKeeper 1.0.0) + - Added ZooKeeper-based implementation ([#41](https://github.com/madelson/DistributedLock/issues/41), DistributedLock.ZooKeeper 1.0.0) - 2.0.2 - - Fixed bug where `HandleLostToken` would hang when accessed on a SqlServer or Postgres lock handle that used keepalive ([#85](https://github.com/madelson/DistributedLock/issues/85), DistributedLock.Core 1.0.1) - - Fixed bug where broken database connections could result in future lock attempts failing when using SqlServer or Postgres locks with multiplexing ([#83](https://github.com/madelson/DistributedLock/issues/83), DistributedLock.Core 1.0.1) - - Updated Npgsql dependency to 5.x to take advantage of various bugfixes ([#61](https://github.com/madelson/DistributedLock/issues/61), DistributedLock.Postgres 1.0.1) + - Fixed bug where `HandleLostToken` would hang when accessed on a SqlServer or Postgres lock handle that used keepalive ([#85](https://github.com/madelson/DistributedLock/issues/85), DistributedLock.Core 1.0.1) + - Fixed bug where broken database connections could result in future lock attempts failing when using SqlServer or Postgres locks with multiplexing ([#83](https://github.com/madelson/DistributedLock/issues/83), DistributedLock.Core 1.0.1) + - Updated Npgsql dependency to 5.x to take advantage of various bugfixes ([#61](https://github.com/madelson/DistributedLock/issues/61), DistributedLock.Postgres 1.0.1) - 2.0.1 - - Fixed Redis lock behavior when using a database with `WithKeyPrefix` ([#66](https://github.com/madelson/DistributedLock/issues/66), DistributedLock.Redis 1.0.1). Thanks [@skomis-mm](https://github.com/skomis-mm) for contributing! + - Fixed Redis lock behavior when using a database with `WithKeyPrefix` ([#66](https://github.com/madelson/DistributedLock/issues/66), DistributedLock.Redis 1.0.1). Thanks [@skomis-mm](https://github.com/skomis-mm) for contributing! - 2.0.0 (see also [Migrating from 1.x to 2.x](docs/Migrating%20from%201.x%20to%202.x.md#migrating-from-1x-to-2x)) - - Revamped package structure so that DistributedLock is now an umbrella package and each implementation technology has its own package (BREAKING CHANGE) - - Added Postgresql-based locking ([#56](https://github.com/madelson/DistributedLock/issues/56), DistributedLock.Postgres 1.0.0) - - Added Redis-based locking ([#24](https://github.com/madelson/DistributedLock/issues/24), DistributedLock.Redis 1.0.0) - - Added Azure blob-based locking ([#42](https://github.com/madelson/DistributedLock/issues/42), DistributedLock.Azure 1.0.0) - - Added file-based locking ([#28](https://github.com/madelson/DistributedLock/issues/28), DistributedLock.FileSystem 1.0.0) - - Added provider classes for improved IOC integration ([#13](https://github.com/madelson/DistributedLock/issues/13)) - - Added strong naming to assemblies. Thanks [@pedropaulovc](https://github.com/pedropaulovc) for contributing! ([#47](https://github.com/madelson/DistributedLock/issues/47), BREAKING CHANGE) - - Made lock handles implement `IAsyncDisposable` in addition to `IDisposable` [#20](https://github.com/madelson/DistributedLock/issues/20), BREAKING CHANGE) - - Exposed implementation-agnostic interfaces (e. g. `IDistributedLock`) for all synchronization primitives ([#10](https://github.com/madelson/DistributedLock/issues/10)) - - Added `HandleLostToken` API for tracking if a lock's underlying connection dies ([#6](https://github.com/madelson/DistributedLock/issues/6), BREAKING CHANGE) - - Added SourceLink support ([#57](https://github.com/madelson/DistributedLock/issues/57)) - - Removed `GetSafeName` API in favor of safe naming by default (BREAKING CHANGE) - - Renamed "SystemDistributedLock" to "EventWaitHandleDistributedLock" (DistributedLock.WaitHandles 1.0.0) - - Stopped supporting net45 (BREAKING CHANGE) - - Removed `DbConnection` and `DbTransaction` constructors form `SqlDistributedLock`, leaving the constructors that take `IDbConnection`/`IDbTransaction` ([#35](https://github.com/madelson/DistributedLock/issues/35), BREAKING CHANGE) - - Changed methods returning `Task` to instead return `ValueTask`, making it so that `using (@lock.AcquireAsync()) { ... } without an `await` no longer compiles (#34, BREAKING CHANGE) - - Changed `UpgradeableLockHandle.UpgradeToWriteLock` to return `void` ([#33](https://github.com/madelson/DistributedLock/issues/33), BREAKING CHANGE) - - Switched to Microsoft.Data.SqlClient by default for all target frameworks (BREAKING CHANGE) - - Changed all locking implementations to be non-reentrant (BREAKING CHANGE) + - Revamped package structure so that DistributedLock is now an umbrella package and each implementation technology has its own package (BREAKING CHANGE) + - Added Postgresql-based locking ([#56](https://github.com/madelson/DistributedLock/issues/56), DistributedLock.Postgres 1.0.0) + - Added Redis-based locking ([#24](https://github.com/madelson/DistributedLock/issues/24), DistributedLock.Redis 1.0.0) + - Added Azure blob-based locking ([#42](https://github.com/madelson/DistributedLock/issues/42), DistributedLock.Azure 1.0.0) + - Added file-based locking ([#28](https://github.com/madelson/DistributedLock/issues/28), DistributedLock.FileSystem 1.0.0) + - Added provider classes for improved IOC integration ([#13](https://github.com/madelson/DistributedLock/issues/13)) + - Added strong naming to assemblies. Thanks [@pedropaulovc](https://github.com/pedropaulovc) for contributing! ([#47](https://github.com/madelson/DistributedLock/issues/47), BREAKING CHANGE) + - Made lock handles implement `IAsyncDisposable` in addition to `IDisposable` [#20](https://github.com/madelson/DistributedLock/issues/20), BREAKING CHANGE) + - Exposed implementation-agnostic interfaces (e. g. `IDistributedLock`) for all synchronization primitives ([#10](https://github.com/madelson/DistributedLock/issues/10)) + - Added `HandleLostToken` API for tracking if a lock's underlying connection dies ([#6](https://github.com/madelson/DistributedLock/issues/6), BREAKING CHANGE) + - Added SourceLink support ([#57](https://github.com/madelson/DistributedLock/issues/57)) + - Removed `GetSafeName` API in favor of safe naming by default (BREAKING CHANGE) + - Renamed "SystemDistributedLock" to "EventWaitHandleDistributedLock" (DistributedLock.WaitHandles 1.0.0) + - Stopped supporting net45 (BREAKING CHANGE) + - Removed `DbConnection` and `DbTransaction` constructors form `SqlDistributedLock`, leaving the constructors that take `IDbConnection`/`IDbTransaction` ([#35](https://github.com/madelson/DistributedLock/issues/35), BREAKING CHANGE) + - Changed methods returning `Task` to instead return `ValueTask`, making it so that `using (@lock.AcquireAsync()) { ... } without an `await` no longer compiles (#34, BREAKING CHANGE) + - Changed `UpgradeableLockHandle.UpgradeToWriteLock` to return `void` ([#33](https://github.com/madelson/DistributedLock/issues/33), BREAKING CHANGE) + - Switched to Microsoft.Data.SqlClient by default for all target frameworks (BREAKING CHANGE) + - Changed all locking implementations to be non-reentrant (BREAKING CHANGE) - 1.5.0 - - Added cross-platform support via Microsoft.Data.SqlClient ([#25](https://github.com/madelson/DistributedLock/issues/25)). This feature is available for .NET Standard >= 2.0. Thanks to [@alesebi91](https://github.com/alesebi91) for helping with the implementation and testing! - - Added C#8 nullable annotations ([#31](https://github.com/madelson/DistributedLock/issues/31)) - - Fixed minor bug in connection multiplexing which could lead to more lock contention ([#32](https://github.com/madelson/DistributedLock/issues/32)) + - Added cross-platform support via Microsoft.Data.SqlClient ([#25](https://github.com/madelson/DistributedLock/issues/25)). This feature is available for .NET Standard >= 2.0. Thanks to [@alesebi91](https://github.com/alesebi91) for helping with the implementation and testing! + - Added C#8 nullable annotations ([#31](https://github.com/madelson/DistributedLock/issues/31)) + - Fixed minor bug in connection multiplexing which could lead to more lock contention ([#32](https://github.com/madelson/DistributedLock/issues/32)) - 1.4.0 - - Added a SQL-based distributed semaphore ([#7](https://github.com/madelson/DistributedLock/issues/7)) - - Fix bug where SqlDistributedLockConnectionStrategy.Azure would leak connections, relying on GC to reclaim them ([#14](https://github.com/madelson/DistributedLock/issues/14)). Thanks [zavalita1](https://github.com/zavalita1) for investigating this issue! - - Throw a specific exception type (`DeadlockException`) rather than the generic `InvalidOperationException` when a deadlock is detected ([#11](https://github.com/madelson/DistributedLock/issues/11)) + - Added a SQL-based distributed semaphore ([#7](https://github.com/madelson/DistributedLock/issues/7)) + - Fix bug where SqlDistributedLockConnectionStrategy.Azure would leak connections, relying on GC to reclaim them ([#14](https://github.com/madelson/DistributedLock/issues/14)). Thanks [zavalita1](https://github.com/zavalita1) for investigating this issue! + - Throw a specific exception type (`DeadlockException`) rather than the generic `InvalidOperationException` when a deadlock is detected ([#11](https://github.com/madelson/DistributedLock/issues/11)) - 1.3.1 Minor fix to avoid "leaking" isolation level changes in transaction-based locks ([#8](https://github.com/madelson/DistributedLock/issues/8)). Also switched to the VS2017 project file format - 1.3.0 Added an Azure connection strategy to keep lock connections from becoming idle and being reclaimed by Azure's connection governor ([#5](https://github.com/madelson/DistributedLock/issues/5)) - 1.2.0 - - Added a SQL-based distributed reader-writer lock - - .NET Core support via .NET Standard - - Changed the default locking scope for SQL distributed lock to be a connection rather than a transaction, avoiding cases where long-running transactions can block backups - - Allowed for customization of the SQL distributed lock connection strategy when connecting via a connection string - - Added a new connection strategy which allows for multiplexing multiple held locks onto one connection - - Added IDbConnection/IDbTransaction constructors ([#3](https://github.com/madelson/DistributedLock/issues/3)) + - Added a SQL-based distributed reader-writer lock + - .NET Core support via .NET Standard + - Changed the default locking scope for SQL distributed lock to be a connection rather than a transaction, avoiding cases where long-running transactions can block backups + - Allowed for customization of the SQL distributed lock connection strategy when connecting via a connection string + - Added a new connection strategy which allows for multiplexing multiple held locks onto one connection + - Added IDbConnection/IDbTransaction constructors ([#3](https://github.com/madelson/DistributedLock/issues/3)) - 1.1.0 Added support for SQL distributed locks scoped to existing connections/transactions - 1.0.1 Minor fix when using infinite timeouts - 1.0.0 Initial release diff --git a/docs/DistributedLock.MongoDB.md b/docs/DistributedLock.MongoDB.md new file mode 100644 index 00000000..231bd594 --- /dev/null +++ b/docs/DistributedLock.MongoDB.md @@ -0,0 +1,82 @@ +# DistributedLock.MongoDB + +[Download the NuGet package](https://www.nuget.org/packages/DistributedLock.MongoDB) [![NuGet Status](http://img.shields.io/nuget/v/DistributedLock.MongoDB.svg?style=flat)](https://www.nuget.org/packages/DistributedLock.MongoDB/) + +The DistributedLock.MongoDB package offers distributed locks based on [MongoDB](https://www.mongodb.com/). For example: + +```C# +var client = new MongoClient("mongodb://localhost:27017"); +var database = client.GetDatabase("myDatabase"); +var @lock = new MongoDistributedLock("MyLockName", database); +await using (await @lock.AcquireAsync()) +{ + // I have the lock +} +``` + +## APIs + +- The `MongoDistributedLock` class implements the `IDistributedLock` interface. +- The `MongoDistributedSynchronizationProvider` class implements the `IDistributedLockProvider` interface. + +## Implementation notes + +MongoDB-based locks use MongoDB's document upsert and update operations to implement distributed locking. The implementation works as follows: + +1. **Acquisition**: Attempts to insert or update a document with the lock key and a unique lock ID. +2. **Extension**: Automatically extends the lock expiry while held to prevent timeout. +3. **Release**: Deletes the lock document when disposed. +4. **Expiry**: Locks automatically expire if not extended, allowing recovery from crashed processes. + +MongoDB locks can be constructed with an `IMongoDatabase` and an optional collection name. If no collection name is specified, locks will be stored in a collection named `"DistributedLocks"`. The collection will automatically have an index created on the `expiresAt` field for efficient queries. + +When using the provider pattern, you can create multiple locks with different names from the same provider: + +```C# +var client = new MongoClient(connectionString); +var database = client.GetDatabase("myDatabase"); +var provider = new MongoDistributedSynchronizationProvider(database); + +var lock1 = provider.CreateLock("lock1"); +var lock2 = provider.CreateLock("lock2"); + +await using (await lock1.AcquireAsync()) +{ + // Do work with lock1 +} +``` + +**NOTE**: Lock extension happens automatically in the background while the lock is held. If lock extension fails (for example, due to network issues), the `HandleLostToken` will be signaled to notify you that the lock may have been lost. + +## Options + +In addition to specifying the name and database, several tuning options are available: + +- `Expiry` determines how long the lock will be initially claimed for. Because of automatic extension, locks can be held for longer than this value. Defaults to 30 seconds. +- `ExtensionCadence` determines how frequently the hold on the lock will be renewed to the full `Expiry`. Defaults to 1/3 of `Expiry` (approximately 10 seconds when using the default expiry). +- `BusyWaitSleepTime` specifies a range of times that the implementation will sleep between attempts to acquire a lock that is currently held by someone else. A random time in the range will be chosen for each sleep. If you expect contention, lowering these values may increase responsiveness (how quickly a lock detects that it can now be taken) but will increase the number of calls made to MongoDB. Raising the values will have the reverse effects. Defaults to a range of 10ms to 800ms. + +Example of using options: + +```C# +var @lock = new MongoDistributedLock( + "MyLockName", + database, + options => options + .Expiry(TimeSpan.FromSeconds(30)) + .ExtensionCadence(TimeSpan.FromSeconds(10)) + .BusyWaitSleepTime( + min: TimeSpan.FromMilliseconds(10), + max: TimeSpan.FromMilliseconds(800)) +); +``` + +You can also specify a custom collection name: + +```C# +var @lock = new MongoDistributedLock("MyLockName", database, "MyCustomLocks"); +``` + +## Stale lock cleanup + +Stale locks from crashed processes will automatically expire based on the `Expiry` setting. MongoDB's built-in TTL index support ensures that expired lock documents are cleaned up automatically by the database. This means that if a process crashes while holding a lock, the lock will become available again after the expiry time has elapsed. From 9b4ddf4392bd7787470bad97308ef934e63920b6 Mon Sep 17 00:00:00 2001 From: Joes Date: Wed, 22 Oct 2025 09:50:05 +0800 Subject: [PATCH 08/21] fix: ci --- .../Tests/MongoDB/MongoDistributedLockTest.cs | 2 ++ .../MongoDistributedSynchronizationOptionsBuilderTest.cs | 1 - .../MongoDB/MongoDistributedSynchronizationProviderTest.cs | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs index a8f8ef1e..9c753478 100644 --- a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs +++ b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs @@ -97,6 +97,7 @@ public async Task TestLockContentionAsync() } [Test] + [Category("CI")] public void TestName() { const string Name = "\0🐉汉字\b\r\n\\"; @@ -107,6 +108,7 @@ public void TestName() } [Test] + [Category("CI")] public void TestValidatesConstructorParameters() { var database = new Mock(MockBehavior.Strict).Object; diff --git a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationOptionsBuilderTest.cs b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationOptionsBuilderTest.cs index c6d4a430..1e2ae9c0 100644 --- a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationOptionsBuilderTest.cs +++ b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationOptionsBuilderTest.cs @@ -3,7 +3,6 @@ namespace Medallion.Threading.Tests.MongoDB; -[Category("CI")] public class MongoDistributedSynchronizationOptionsBuilderTest { [Test] diff --git a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationProviderTest.cs b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationProviderTest.cs index 54879238..1aa97e1e 100644 --- a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationProviderTest.cs +++ b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedSynchronizationProviderTest.cs @@ -4,7 +4,6 @@ namespace Medallion.Threading.Tests.MongoDB; -[Category("CI")] public class MongoDistributedSynchronizationProviderTest { [Test] From e26097973c930b8f1e5ee7691c6fe4a8cbd50020 Mon Sep 17 00:00:00 2001 From: Joes Date: Thu, 23 Oct 2025 09:43:13 +0800 Subject: [PATCH 09/21] =?UTF-8?q?tests:=20=E5=9C=A8=20CombinatorialTests.c?= =?UTF-8?q?s=20=E4=B8=AD=E6=B7=BB=E5=8A=A0=20MongoDB=20=E7=BB=84=E5=90=88?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=B1=BB=20Core=5FMongo=5FMongoDbSynchroniza?= =?UTF-8?q?tionStrategyTest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DistributedLock.Tests/Tests/CombinatorialTests.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/DistributedLock.Tests/Tests/CombinatorialTests.cs b/src/DistributedLock.Tests/Tests/CombinatorialTests.cs index cfa4c9ab..90dd2ca3 100644 --- a/src/DistributedLock.Tests/Tests/CombinatorialTests.cs +++ b/src/DistributedLock.Tests/Tests/CombinatorialTests.cs @@ -11,6 +11,11 @@ namespace Medallion.Threading.Tests.FileSystem [Category("CI")] public class Core_File_FileSynchronizationStrategyTest : DistributedLockCoreTestCases { } } +namespace Medallion.Threading.Tests.MongoDB +{ + public class Core_Mongo_MongoDbSynchronizationStrategyTest : DistributedLockCoreTestCases { } +} + namespace Medallion.Threading.Tests.MySql { public class ConnectionStringStrategy_MySql_ConnectionMultiplexingSynchronizationStrategy_MariaDbDb_MariaDbDb_ConnectionMultiplexingSynchronizationStrategy_MariaDbDb_MariaDbDbTest : ConnectionStringStrategyTestCases, TestingMariaDbDb>, TestingConnectionMultiplexingSynchronizationStrategy, TestingMariaDbDb> { } From 3a28ef0428579e4dc080ae8b281ac9adddc329af Mon Sep 17 00:00:00 2001 From: Joes Date: Thu, 23 Oct 2025 10:50:30 +0800 Subject: [PATCH 10/21] fix: ci error? MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 `MongoDistributedLock.IDistributedLock.cs` 文件中,`MongoDistributedLock` 类实现了 `IDistributedLock` 接口的 `TryAcquire` 方法,新增了自动生成的方法以支持分布式锁的获取。此外,更新了文档注释,修正了异步获取锁的代码示例格式。 --- .../MongoDistributedLock.IDistributedLock.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs b/src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs index 89936ac7..b2fd5714 100644 --- a/src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs +++ b/src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs @@ -4,6 +4,8 @@ namespace Medallion.Threading.MongoDB; public partial class MongoDistributedLock { + // AUTO-GENERATED + IDistributedSynchronizationHandle? IDistributedLock.TryAcquire(TimeSpan timeout, CancellationToken cancellationToken) { return TryAcquire(timeout, cancellationToken); @@ -84,7 +86,7 @@ public MongoDistributedLockHandle Acquire(TimeSpan? timeout = null, Cancellation /// await using (await myLock.AcquireAsync(...)) /// { /// /* we have the lock! */ - /// } + /// } /// // dispose releases the lock /// /// From cd141257708fc80244263996f0ef27b83f2ce396 Mon Sep 17 00:00:00 2001 From: Joes Date: Thu, 23 Oct 2025 10:54:40 +0800 Subject: [PATCH 11/21] style: fix code format --- .../MongoDistributedLock.IDistributedLock.cs | 71 +++++++------------ 1 file changed, 26 insertions(+), 45 deletions(-) diff --git a/src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs b/src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs index b2fd5714..33ffa43a 100644 --- a/src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs +++ b/src/DistributedLock.MongoDB/MongoDistributedLock.IDistributedLock.cs @@ -6,28 +6,17 @@ public partial class MongoDistributedLock { // AUTO-GENERATED - IDistributedSynchronizationHandle? IDistributedLock.TryAcquire(TimeSpan timeout, CancellationToken cancellationToken) - { - return TryAcquire(timeout, cancellationToken); - } - - IDistributedSynchronizationHandle IDistributedLock.Acquire(TimeSpan? timeout, CancellationToken cancellationToken) - { - return Acquire(timeout, cancellationToken); - } - - ValueTask IDistributedLock.TryAcquireAsync(TimeSpan timeout, CancellationToken cancellationToken) - { - return TryAcquireAsync(timeout, cancellationToken).Convert(To.ValueTask); - } - - ValueTask IDistributedLock.AcquireAsync(TimeSpan? timeout, CancellationToken cancellationToken) - { - return AcquireAsync(timeout, cancellationToken).Convert(To.ValueTask); - } + IDistributedSynchronizationHandle? IDistributedLock.TryAcquire(TimeSpan timeout, CancellationToken cancellationToken) => + this.TryAcquire(timeout, cancellationToken); + IDistributedSynchronizationHandle IDistributedLock.Acquire(TimeSpan? timeout, CancellationToken cancellationToken) => + this.Acquire(timeout, cancellationToken); + ValueTask IDistributedLock.TryAcquireAsync(TimeSpan timeout, CancellationToken cancellationToken) => + this.TryAcquireAsync(timeout, cancellationToken).Convert(To.ValueTask); + ValueTask IDistributedLock.AcquireAsync(TimeSpan? timeout, CancellationToken cancellationToken) => + this.AcquireAsync(timeout, cancellationToken).Convert(To.ValueTask); /// - /// Attempts to acquire the lock synchronously. Usage: + /// Attempts to acquire the lock synchronously. Usage: /// /// using (var handle = myLock.TryAcquire(...)) /// { @@ -38,14 +27,12 @@ ValueTask IDistributedLock.AcquireAsync(TimeS /// /// How long to wait before giving up on the acquisition attempt. Defaults to 0 /// Specifies a token by which the wait can be canceled - /// A which can be used to release the lock or null on failure - public MongoDistributedLockHandle? TryAcquire(TimeSpan timeout = default, CancellationToken cancellationToken = default) - { - return DistributedLockHelpers.TryAcquire(this, timeout, cancellationToken); - } + /// A which can be used to release the lock or null on failure + public MongoDistributedLockHandle? TryAcquire(TimeSpan timeout = default, CancellationToken cancellationToken = default) => + DistributedLockHelpers.TryAcquire(this, timeout, cancellationToken); /// - /// Acquires the lock synchronously, failing with if the attempt times out. Usage: + /// Acquires the lock synchronously, failing with if the attempt times out. Usage: /// /// using (myLock.Acquire(...)) /// { @@ -54,16 +41,14 @@ ValueTask IDistributedLock.AcquireAsync(TimeS /// // dispose releases the lock /// /// - /// How long to wait before giving up on the acquisition attempt. Defaults to + /// How long to wait before giving up on the acquisition attempt. Defaults to /// Specifies a token by which the wait can be canceled - /// A which can be used to release the lock - public MongoDistributedLockHandle Acquire(TimeSpan? timeout = null, CancellationToken cancellationToken = default) - { - return DistributedLockHelpers.Acquire(this, timeout, cancellationToken); - } + /// A which can be used to release the lock + public MongoDistributedLockHandle Acquire(TimeSpan? timeout = null, CancellationToken cancellationToken = default) => + DistributedLockHelpers.Acquire(this, timeout, cancellationToken); /// - /// Attempts to acquire the lock asynchronously. Usage: + /// Attempts to acquire the lock asynchronously. Usage: /// /// await using (var handle = await myLock.TryAcquireAsync(...)) /// { @@ -74,14 +59,12 @@ public MongoDistributedLockHandle Acquire(TimeSpan? timeout = null, Cancellation /// /// How long to wait before giving up on the acquisition attempt. Defaults to 0 /// Specifies a token by which the wait can be canceled - /// A which can be used to release the lock or null on failure - public ValueTask TryAcquireAsync(TimeSpan timeout = default, CancellationToken cancellationToken = default) - { - return this.As>().InternalTryAcquireAsync(timeout, cancellationToken); - } + /// A which can be used to release the lock or null on failure + public ValueTask TryAcquireAsync(TimeSpan timeout = default, CancellationToken cancellationToken = default) => + this.As>().InternalTryAcquireAsync(timeout, cancellationToken); /// - /// Acquires the lock asynchronously, failing with if the attempt times out. Usage: + /// Acquires the lock asynchronously, failing with if the attempt times out. Usage: /// /// await using (await myLock.AcquireAsync(...)) /// { @@ -90,11 +73,9 @@ public MongoDistributedLockHandle Acquire(TimeSpan? timeout = null, Cancellation /// // dispose releases the lock /// /// - /// How long to wait before giving up on the acquisition attempt. Defaults to + /// How long to wait before giving up on the acquisition attempt. Defaults to /// Specifies a token by which the wait can be canceled - /// A which can be used to release the lock - public ValueTask AcquireAsync(TimeSpan? timeout = null, CancellationToken cancellationToken = default) - { - return DistributedLockHelpers.AcquireAsync(this, timeout, cancellationToken); - } + /// A which can be used to release the lock + public ValueTask AcquireAsync(TimeSpan? timeout = null, CancellationToken cancellationToken = default) => + DistributedLockHelpers.AcquireAsync(this, timeout, cancellationToken); } \ No newline at end of file From ba277b3ba751e7b65527f600e2c19d7a57070bff Mon Sep 17 00:00:00 2001 From: Joes Date: Wed, 17 Dec 2025 18:05:16 +0800 Subject: [PATCH 12/21] chore: Performance Optimization --- src/Directory.Packages.props | 2 +- src/DistributedLock.Core/packages.lock.json | 6 +- .../MongoDistributedLock.cs | 154 ++++++++++++------ .../MongoDistributedLockHandle.cs | 91 +++++++---- .../packages.lock.json | 42 ++--- .../packages.lock.json | 6 +- src/DistributedLock.Tests/packages.lock.json | 16 +- src/DistributedLock/packages.lock.json | 28 ++-- src/DistributedLockTaker/packages.lock.json | 38 ++--- 9 files changed, 235 insertions(+), 148 deletions(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 2894c36a..c8bdc85e 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -7,7 +7,7 @@ - + diff --git a/src/DistributedLock.Core/packages.lock.json b/src/DistributedLock.Core/packages.lock.json index 0e0884af..86c0653a 100644 --- a/src/DistributedLock.Core/packages.lock.json +++ b/src/DistributedLock.Core/packages.lock.json @@ -148,9 +148,9 @@ "net8.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.21, )", - "resolved": "8.0.21", - "contentHash": "s8H5PZQs50OcNkaB6Si54+v3GWM7vzs6vxFRMlD3aXsbM+aPCtod62gmK0BYWou9diGzmo56j8cIf/PziijDqQ==" + "requested": "[8.0.22, )", + "resolved": "8.0.22", + "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/src/DistributedLock.MongoDB/MongoDistributedLock.cs b/src/DistributedLock.MongoDB/MongoDistributedLock.cs index 1fb4ec8d..ab2e2554 100644 --- a/src/DistributedLock.MongoDB/MongoDistributedLock.cs +++ b/src/DistributedLock.MongoDB/MongoDistributedLock.cs @@ -1,5 +1,7 @@ using Medallion.Threading.Internal; +using MongoDB.Bson; using MongoDB.Driver; +using System.Collections.Concurrent; namespace Medallion.Threading.MongoDB; @@ -8,6 +10,13 @@ namespace Medallion.Threading.MongoDB; /// public sealed partial class MongoDistributedLock : IInternalDistributedLock { +#if !NETSTANDARD2_1_OR_GREATER && !NET8_0_OR_GREATER + private static readonly DateTime EpochUtc = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); +#endif + + // We want to ensure indexes at most once per process per (db, collection) + private static readonly ConcurrentDictionary> IndexInitializationTasks = new(StringComparer.Ordinal); + private readonly string _collectionName; private readonly IMongoDatabase _database; private readonly MongoDistributedLockOptions _options; @@ -20,7 +29,7 @@ public sealed partial class MongoDistributedLock : IInternalDistributedLock /// Implements /// - public string Name => Key; + public string Name => this.Key; /// /// Constructs a lock named using the provided and . @@ -36,10 +45,10 @@ public MongoDistributedLock(string key, IMongoDatabase database, Action? options = null) { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException(nameof(key)); } - _database = database ?? throw new ArgumentNullException(nameof(database)); - _collectionName = collectionName ?? throw new ArgumentNullException(nameof(collectionName)); - Key = key; - _options = MongoDistributedSynchronizationOptionsBuilder.GetOptions(options); + this._database = database ?? throw new ArgumentNullException(nameof(database)); + this._collectionName = collectionName ?? throw new ArgumentNullException(nameof(collectionName)); + this.Key = key; + this._options = MongoDistributedSynchronizationOptionsBuilder.GetOptions(options); } ValueTask IInternalDistributedLock.InternalTryAcquireAsync(TimeoutValue timeout, CancellationToken cancellationToken) @@ -47,70 +56,117 @@ public MongoDistributedLock(string key, IMongoDatabase database, string collecti return BusyWaitHelper.WaitAsync(this, (@this, ct) => @this.TryAcquireAsync(ct), timeout, - _options.MinBusyWaitSleepTime, - _options.MaxBusyWaitSleepTime, + this._options.MinBusyWaitSleepTime, + this._options.MaxBusyWaitSleepTime, cancellationToken); } private async ValueTask TryAcquireAsync(CancellationToken cancellationToken) { - var collection = _database.GetCollection(_collectionName); - - // Ensure index exists for efficient queries - await EnsureIndexAsync(collection, cancellationToken).ConfigureAwait(false); - var lockId = Guid.NewGuid().ToString(); - var now = DateTime.UtcNow; - var expiresAt = now.Add(_options.Expiry.TimeSpan); - - // Filter: lock with this key doesn't exist OR has expired - var filter = Builders.Filter.Or(Builders.Filter.Eq(d => d.Id, Key) & Builders.Filter.Lt(d => d.ExpiresAt, now), - Builders.Filter.Eq(d => d.Id, Key) & Builders.Filter.Exists(d => d.ExpiresAt, false)); - var update = Builders.Update - .Set(d => d.Id, Key) - .Set(d => d.LockId, lockId) - .Set(d => d.ExpiresAt, expiresAt) - .Set(d => d.AcquiredAt, now); - var options = new FindOneAndUpdateOptions { IsUpsert = true, ReturnDocument = ReturnDocument.After }; - try - { - var result = await collection.FindOneAndUpdateAsync(filter, update, options, cancellationToken).ConfigureAwait(false); + var collection = this._database.GetCollection(this._collectionName); - // Verify we actually got the lock - if (result != null && result.LockId == lockId) - { - return new(_database, - _collectionName, - Key, - lockId, - _options.Expiry, - _options.ExtensionCadence); - } - } - catch (MongoWriteException ex) when (ex.WriteError?.Category == ServerErrorCategory.DuplicateKey) + // Ensure indexes exist (TTL cleanup); do this at most once per process per (db, collection) + await EnsureIndexesCreatedAsync(collection).ConfigureAwait(false); + + // Use a unique token per acquisition attempt (like Redis' value token) + var lockId = Guid.NewGuid().ToString("N"); + var expiryMs = this._options.Expiry.InMilliseconds; + + // We avoid exception-driven contention (DuplicateKey) by using a single upsert on {_id == Key} + // and an update pipeline that only overwrites fields when the existing lock is expired. + // This is conceptually similar to Redis: SET key value NX PX . + var filter = Builders.Filter.Eq(d => d.Id, this.Key); + var update = CreateAcquireUpdate(lockId, expiryMs); + var options = new FindOneAndUpdateOptions { - // Lock is already held by someone else - return null; - } - catch (MongoCommandException) + IsUpsert = true, + ReturnDocument = ReturnDocument.After + }; + + var result = await collection.FindOneAndUpdateAsync(filter, update, options, cancellationToken).ConfigureAwait(false); + + // Verify we actually got the lock + if (result != null && result.LockId == lockId) { - // Lock is already held - return null; + return new(collection, + this.Key, + lockId, + this._options.Expiry, + this._options.ExtensionCadence); } + return null; } - private static async Task EnsureIndexAsync(IMongoCollection collection, CancellationToken cancellationToken) + private static UpdateDefinition CreateAcquireUpdate(string lockId, int expiryMs) + { + // expired := ifNull(expiresAt, epoch) <= $$NOW + var expiredOrMissing = new BsonDocument( + "$lte", + new BsonArray + { + new BsonDocument("$ifNull", new BsonArray { "$expiresAt", new BsonDateTime( +#if NETSTANDARD2_1_OR_GREATER || NET8_0_OR_GREATER + DateTime.UnixEpoch +#else + EpochUtc +#endif + ) }), + "$$NOW" + } + ); + + var newExpiresAt = new BsonDocument( + "$dateAdd", + new BsonDocument + { + { "startDate", "$$NOW" }, + { "unit", "millisecond" }, + { "amount", expiryMs } + } + ); + + var setStage = new BsonDocument( + "$set", + new BsonDocument + { + // Only overwrite lock fields when the previous lock is expired/missing + { "lockId", new BsonDocument("$cond", new BsonArray { expiredOrMissing, lockId, "$lockId" }) }, + { "expiresAt", new BsonDocument("$cond", new BsonArray { expiredOrMissing, newExpiresAt, "$expiresAt" }) }, + { "acquiredAt", new BsonDocument("$cond", new BsonArray { expiredOrMissing, "$$NOW", "$acquiredAt" }) } + } + ); + + return new PipelineUpdateDefinition(new[] { setStage }); + } + + private static Task EnsureIndexesCreatedAsync(IMongoCollection collection) + { + // Best-effort TTL index to clean up expired rows over time. + // Note: TTL monitors run on a schedule; correctness MUST NOT depend on this. + var databaseName = collection.Database.DatabaseNamespace.DatabaseName; + var key = databaseName + "/" + collection.CollectionNamespace.CollectionName; + + var lazy = IndexInitializationTasks.GetOrAdd(key, _ => new Lazy(() => CreateIndexesAsync(collection))); + return lazy.Value; + } + + private static async Task CreateIndexesAsync(IMongoCollection collection) { try { var indexKeys = Builders.IndexKeys.Ascending(d => d.ExpiresAt); - var indexOptions = new CreateIndexOptions { Background = true }; + var indexOptions = new CreateIndexOptions + { + // TTL cleanup: remove documents once expiresAt < now + ExpireAfter = TimeSpan.Zero, + }; var indexModel = new CreateIndexModel(indexKeys, indexOptions); - await collection.Indexes.CreateOneAsync(indexModel, cancellationToken: cancellationToken).ConfigureAwait(false); + await collection.Indexes.CreateOneAsync(indexModel, cancellationToken: CancellationToken.None).ConfigureAwait(false); } catch { - // Index might already exist, ignore errors + // Index may already exist, or server may reject options (e.g., conflicts). Ignore. } } } \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs b/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs index 352c698c..ee8f96d8 100644 --- a/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs +++ b/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs @@ -1,4 +1,5 @@ using Medallion.Threading.Internal; +using MongoDB.Bson; using MongoDB.Driver; namespace Medallion.Threading.MongoDB; @@ -8,9 +9,8 @@ namespace Medallion.Threading.MongoDB; /// public sealed class MongoDistributedLockHandle : IDistributedSynchronizationHandle { - private readonly string _collectionName; private readonly CancellationTokenSource _cts; - private readonly IMongoDatabase _database; + private readonly IMongoCollection _collection; private readonly Task _extensionTask; private readonly string _key; private readonly string _lockId; @@ -19,24 +19,22 @@ public sealed class MongoDistributedLockHandle : IDistributedSynchronizationHand /// /// Implements /// - public CancellationToken HandleLostToken => _cts.Token; + public CancellationToken HandleLostToken => this._cts.Token; internal MongoDistributedLockHandle( - IMongoDatabase database, - string collectionName, + IMongoCollection collection, string key, string lockId, TimeoutValue expiry, TimeoutValue extensionCadence) { - _database = database; - _collectionName = collectionName; - _key = key; - _lockId = lockId; - _cts = new(); + this._collection = collection; + this._key = key; + this._lockId = lockId; + this._cts = new(); // Start background task to extend the lock - _extensionTask = ExtendLockAsync(expiry, extensionCadence, _cts.Token); + this._extensionTask = this.ExtendLockAsync(expiry, extensionCadence, this._cts.Token); } /// @@ -44,14 +42,16 @@ internal MongoDistributedLockHandle( /// public void Dispose() { - if (Interlocked.Exchange(ref _disposed, 1) is not 0) + if (Interlocked.Exchange(ref this._disposed, 1) is not 0) { return; } - _cts.Cancel(); + + this._cts.Cancel(); try { - _extensionTask.Wait(HandleLostToken); + // Do not use HandleLostToken here: it is backed by _cts and has been canceled above. + this._extensionTask.GetAwaiter().GetResult(); } catch { @@ -59,8 +59,15 @@ public void Dispose() } finally { - _cts.Dispose(); - ReleaseLockAsync(CancellationToken.None).AsTask().Wait(HandleLostToken); + this._cts.Dispose(); + try + { + this.ReleaseLockAsync(CancellationToken.None).AsTask().GetAwaiter().GetResult(); + } + catch + { + // Ignore errors during release + } } } @@ -69,12 +76,16 @@ public void Dispose() /// public async ValueTask DisposeAsync() { - if (Interlocked.Exchange(ref _disposed, 1) is 0) + if (Interlocked.Exchange(ref this._disposed, 1) is 0) { - _cts.Cancel(); +#if NET8_0_OR_GREATER + await this._cts.CancelAsync(); +#else + this._cts.Cancel(); +#endif try { - await _extensionTask.ConfigureAwait(false); + await this._extensionTask.ConfigureAwait(false); } catch { @@ -82,8 +93,8 @@ public async ValueTask DisposeAsync() } finally { - _cts.Dispose(); - await ReleaseLockAsync(CancellationToken.None).ConfigureAwait(false); + this._cts.Dispose(); + await this.ReleaseLockAsync(CancellationToken.None).ConfigureAwait(false); } } } @@ -95,17 +106,34 @@ private async Task ExtendLockAsync(TimeoutValue expiry, TimeoutValue extensionCa while (!cancellationToken.IsCancellationRequested) { await Task.Delay(extensionCadence.TimeSpan, cancellationToken).ConfigureAwait(false); - var collection = _database.GetCollection(_collectionName); - var filter = Builders.Filter.Eq(d => d.Id, _key) & Builders.Filter.Eq(d => d.LockId, _lockId); - var update = Builders.Update.Set(d => d.ExpiresAt, DateTime.UtcNow.Add(expiry.TimeSpan)); - var result = await collection.UpdateOneAsync(filter, update, cancellationToken: cancellationToken).ConfigureAwait(false); + var filter = Builders.Filter.Eq(d => d.Id, this._key) & Builders.Filter.Eq(d => d.LockId, this._lockId); + + // Use server time ($$NOW) for expiry to avoid client clock skew. + var newExpiresAt = new BsonDocument( + "$dateAdd", + new BsonDocument + { + { "startDate", "$$NOW" }, + { "unit", "millisecond" }, + { "amount", expiry.InMilliseconds } + } + ); + var update = new PipelineUpdateDefinition( + new[] { new BsonDocument("$set", new BsonDocument("expiresAt", newExpiresAt)) } + ); + + var result = await this._collection.UpdateOneAsync(filter, update, cancellationToken: cancellationToken).ConfigureAwait(false); // If we failed to extend, the lock was lost if (result.MatchedCount is not 0) { continue; } - _cts.Cancel(); +#if NET8_0_OR_GREATER + await this._cts.CancelAsync(); +#else + this._cts.Cancel(); +#endif break; } } @@ -116,7 +144,11 @@ private async Task ExtendLockAsync(TimeoutValue expiry, TimeoutValue extensionCa catch { // Lock extension failed, signal that the lock is lost - _cts.Cancel(); +#if NET8_0_OR_GREATER + await this._cts.CancelAsync(); +#else + this._cts.Cancel(); +#endif } } @@ -124,9 +156,8 @@ private async ValueTask ReleaseLockAsync(CancellationToken cancellationToken) { try { - var collection = _database.GetCollection(_collectionName); - var filter = Builders.Filter.Eq(d => d.Id, _key) & Builders.Filter.Eq(d => d.LockId, _lockId); - await collection.DeleteOneAsync(filter, cancellationToken).ConfigureAwait(false); + var filter = Builders.Filter.Eq(d => d.Id, this._key) & Builders.Filter.Eq(d => d.LockId, this._lockId); + await this._collection.DeleteOneAsync(filter, cancellationToken).ConfigureAwait(false); } catch { diff --git a/src/DistributedLock.MongoDB/packages.lock.json b/src/DistributedLock.MongoDB/packages.lock.json index 11abd24a..bfc80002 100644 --- a/src/DistributedLock.MongoDB/packages.lock.json +++ b/src/DistributedLock.MongoDB/packages.lock.json @@ -20,13 +20,13 @@ }, "MongoDB.Driver": { "type": "Direct", - "requested": "[3.5.0, )", - "resolved": "3.5.0", - "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", "dependencies": { "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "3.5.0", + "MongoDB.Bson": "3.5.2", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", @@ -78,8 +78,8 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "3.5.0", - "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" @@ -252,13 +252,13 @@ }, "MongoDB.Driver": { "type": "Direct", - "requested": "[3.5.0, )", - "resolved": "3.5.0", - "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", "dependencies": { "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "3.5.0", + "MongoDB.Bson": "3.5.2", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", @@ -301,8 +301,8 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "3.5.0", - "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" @@ -391,9 +391,9 @@ }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.21, )", - "resolved": "8.0.21", - "contentHash": "s8H5PZQs50OcNkaB6Si54+v3GWM7vzs6vxFRMlD3aXsbM+aPCtod62gmK0BYWou9diGzmo56j8cIf/PziijDqQ==" + "requested": "[8.0.22, )", + "resolved": "8.0.22", + "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", @@ -407,13 +407,13 @@ }, "MongoDB.Driver": { "type": "Direct", - "requested": "[3.5.0, )", - "resolved": "3.5.0", - "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", "dependencies": { "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "3.5.0", + "MongoDB.Bson": "3.5.2", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", @@ -459,8 +459,8 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "3.5.0", - "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" diff --git a/src/DistributedLock.Postgres/packages.lock.json b/src/DistributedLock.Postgres/packages.lock.json index 041eb72b..80b0048d 100644 --- a/src/DistributedLock.Postgres/packages.lock.json +++ b/src/DistributedLock.Postgres/packages.lock.json @@ -518,9 +518,9 @@ }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.21, )", - "resolved": "8.0.21", - "contentHash": "s8H5PZQs50OcNkaB6Si54+v3GWM7vzs6vxFRMlD3aXsbM+aPCtod62gmK0BYWou9diGzmo56j8cIf/PziijDqQ==" + "requested": "[8.0.22, )", + "resolved": "8.0.22", + "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", diff --git a/src/DistributedLock.Tests/packages.lock.json b/src/DistributedLock.Tests/packages.lock.json index 8fcb7525..9178bc4c 100644 --- a/src/DistributedLock.Tests/packages.lock.json +++ b/src/DistributedLock.Tests/packages.lock.json @@ -259,8 +259,8 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "3.5.0", - "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" @@ -523,7 +523,7 @@ "DistributedLock.MySql": "[1.0.2, )", "DistributedLock.Oracle": "[1.0.4, )", "DistributedLock.Postgres": "[1.3.0, )", - "DistributedLock.Redis": "[1.1.0, )", + "DistributedLock.Redis": "[1.1.1, )", "DistributedLock.SqlServer": "[1.0.6, )", "DistributedLock.WaitHandles": "[1.0.1, )", "DistributedLock.ZooKeeper": "[1.0.0, )" @@ -549,7 +549,7 @@ "type": "Project", "dependencies": { "DistributedLock.Core": "[1.0.8, )", - "MongoDB.Driver": "[3.5.0, )" + "MongoDB.Driver": "[3.5.2, )" } }, "distributedlock.mysql": { @@ -629,13 +629,13 @@ }, "MongoDB.Driver": { "type": "CentralTransitive", - "requested": "[3.5.0, )", - "resolved": "3.5.0", - "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", "dependencies": { "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "3.5.0", + "MongoDB.Bson": "3.5.2", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", diff --git a/src/DistributedLock/packages.lock.json b/src/DistributedLock/packages.lock.json index 8d115faf..ad3986a5 100644 --- a/src/DistributedLock/packages.lock.json +++ b/src/DistributedLock/packages.lock.json @@ -763,8 +763,8 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "3.5.0", - "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" @@ -1088,7 +1088,7 @@ "type": "Project", "dependencies": { "DistributedLock.Core": "[1.0.8, )", - "MongoDB.Driver": "[3.5.0, )" + "MongoDB.Driver": "[3.5.2, )" } }, "distributedlock.mysql": { @@ -1168,13 +1168,13 @@ }, "MongoDB.Driver": { "type": "CentralTransitive", - "requested": "[3.5.0, )", - "resolved": "3.5.0", - "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", "dependencies": { "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "3.5.0", + "MongoDB.Bson": "3.5.2", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", @@ -2100,8 +2100,8 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "3.5.0", - "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" @@ -2459,7 +2459,7 @@ "type": "Project", "dependencies": { "DistributedLock.Core": "[1.0.8, )", - "MongoDB.Driver": "[3.5.0, )" + "MongoDB.Driver": "[3.5.2, )" } }, "distributedlock.mysql": { @@ -2546,13 +2546,13 @@ }, "MongoDB.Driver": { "type": "CentralTransitive", - "requested": "[3.5.0, )", - "resolved": "3.5.0", - "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", "dependencies": { "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "3.5.0", + "MongoDB.Bson": "3.5.2", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", diff --git a/src/DistributedLockTaker/packages.lock.json b/src/DistributedLockTaker/packages.lock.json index 0feb1eeb..83e81fa8 100644 --- a/src/DistributedLockTaker/packages.lock.json +++ b/src/DistributedLockTaker/packages.lock.json @@ -172,8 +172,8 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "3.5.0", - "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" @@ -482,7 +482,7 @@ "DistributedLock.MySql": "[1.0.2, )", "DistributedLock.Oracle": "[1.0.4, )", "DistributedLock.Postgres": "[1.3.0, )", - "DistributedLock.Redis": "[1.1.0, )", + "DistributedLock.Redis": "[1.1.1, )", "DistributedLock.SqlServer": "[1.0.6, )", "DistributedLock.WaitHandles": "[1.0.1, )", "DistributedLock.ZooKeeper": "[1.0.0, )" @@ -512,7 +512,7 @@ "type": "Project", "dependencies": { "DistributedLock.Core": "[1.0.8, )", - "MongoDB.Driver": "[3.5.0, )" + "MongoDB.Driver": "[3.5.2, )" } }, "distributedlock.mysql": { @@ -592,13 +592,13 @@ }, "MongoDB.Driver": { "type": "CentralTransitive", - "requested": "[3.5.0, )", - "resolved": "3.5.0", - "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", "dependencies": { "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "3.5.0", + "MongoDB.Bson": "3.5.2", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", @@ -790,9 +790,9 @@ "net8.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.21, )", - "resolved": "8.0.21", - "contentHash": "s8H5PZQs50OcNkaB6Si54+v3GWM7vzs6vxFRMlD3aXsbM+aPCtod62gmK0BYWou9diGzmo56j8cIf/PziijDqQ==" + "requested": "[8.0.22, )", + "resolved": "8.0.22", + "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" }, "Azure.Core": { "type": "Transitive", @@ -964,8 +964,8 @@ }, "MongoDB.Bson": { "type": "Transitive", - "resolved": "3.5.0", - "contentHash": "JGNK6BanLDEifgkvPLqVFCPus5EDCy416pxf1dxUBRSVd3D9+NB3AvMVX190eXlk5/UXuCxpsQv7jWfNKvppBQ==", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", "dependencies": { "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" @@ -1185,7 +1185,7 @@ "DistributedLock.MySql": "[1.0.2, )", "DistributedLock.Oracle": "[1.0.4, )", "DistributedLock.Postgres": "[1.3.0, )", - "DistributedLock.Redis": "[1.1.0, )", + "DistributedLock.Redis": "[1.1.1, )", "DistributedLock.SqlServer": "[1.0.6, )", "DistributedLock.WaitHandles": "[1.0.1, )", "DistributedLock.ZooKeeper": "[1.0.0, )" @@ -1211,7 +1211,7 @@ "type": "Project", "dependencies": { "DistributedLock.Core": "[1.0.8, )", - "MongoDB.Driver": "[3.5.0, )" + "MongoDB.Driver": "[3.5.2, )" } }, "distributedlock.mysql": { @@ -1291,13 +1291,13 @@ }, "MongoDB.Driver": { "type": "CentralTransitive", - "requested": "[3.5.0, )", - "resolved": "3.5.0", - "contentHash": "ST90u7psyMkNNOWFgSkexsrB3kPn7Ynl2DlMFj2rJyYuc6SIxjmzu4ufy51yzM+cPVE1SvVcdb5UFobrRw6cMg==", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", "dependencies": { "DnsClient": "1.6.1", "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "3.5.0", + "MongoDB.Bson": "3.5.2", "SharpCompress": "0.30.1", "Snappier": "1.0.0", "System.Buffers": "4.5.1", From e70c35c15700caf5ba1b8097d1cecaabfb8266c3 Mon Sep 17 00:00:00 2001 From: Joes Date: Wed, 17 Dec 2025 18:18:04 +0800 Subject: [PATCH 13/21] fix: Ensure proper cancellation token usage with ConfigureAwait(false) --- src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs b/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs index ee8f96d8..2c0b694f 100644 --- a/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs +++ b/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs @@ -79,7 +79,7 @@ public async ValueTask DisposeAsync() if (Interlocked.Exchange(ref this._disposed, 1) is 0) { #if NET8_0_OR_GREATER - await this._cts.CancelAsync(); + await this._cts.CancelAsync().ConfigureAwait(false); #else this._cts.Cancel(); #endif @@ -130,7 +130,7 @@ private async Task ExtendLockAsync(TimeoutValue expiry, TimeoutValue extensionCa continue; } #if NET8_0_OR_GREATER - await this._cts.CancelAsync(); + await this._cts.CancelAsync().ConfigureAwait(false); #else this._cts.Cancel(); #endif @@ -145,7 +145,7 @@ private async Task ExtendLockAsync(TimeoutValue expiry, TimeoutValue extensionCa { // Lock extension failed, signal that the lock is lost #if NET8_0_OR_GREATER - await this._cts.CancelAsync(); + await this._cts.CancelAsync().ConfigureAwait(false); #else this._cts.Cancel(); #endif From 2a7ad1581454d64dcdaec24e36a68e95668ce0c3 Mon Sep 17 00:00:00 2001 From: Joes Date: Mon, 5 Jan 2026 14:06:17 +0800 Subject: [PATCH 14/21] Supports Fencing Token and Adaptive Backoff, enhancing robustness This update introduces fencing tokens for MongoDB distributed locks. The token increments automatically upon successful lock acquisition and is exposed to the caller, facilitating idempotency and preventing concurrency issues caused by lock loss. A new UseAdaptiveBackoff configuration is added to support adaptive exponential backoff, improving performance and MongoDB's load handling in high-concurrency scenarios. The auto-renewal mechanism is more robust, supporting retries when encountering a MongoException. Distributed tracing diagnostics are integrated for .NET 8.0+. Compatibility and dependencies are updated to support net10.0. --- .../DistributedLock.MongoDB.csproj | 2 +- .../MongoDistributedLock.cs | 118 +++++++++++++- .../MongoDistributedLockHandle.cs | 67 ++++++-- ...istributedSynchronizationOptionsBuilder.cs | 22 ++- .../MongoLockDocument.cs | 6 + .../PublicAPI.Unshipped.txt | 4 +- .../packages.lock.json | 148 +++++++++--------- 7 files changed, 276 insertions(+), 91 deletions(-) diff --git a/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj b/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj index 7259e5a4..07428793 100644 --- a/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj +++ b/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj @@ -1,7 +1,7 @@ - netstandard2.1;net8.0;net472; + netstandard2.1;net8.0;net10.0;net472; Medallion.Threading.MongoDB True 4 diff --git a/src/DistributedLock.MongoDB/MongoDistributedLock.cs b/src/DistributedLock.MongoDB/MongoDistributedLock.cs index ab2e2554..d9faf44a 100644 --- a/src/DistributedLock.MongoDB/MongoDistributedLock.cs +++ b/src/DistributedLock.MongoDB/MongoDistributedLock.cs @@ -3,6 +3,10 @@ using MongoDB.Driver; using System.Collections.Concurrent; +#if NET8_0_OR_GREATER +using System.Diagnostics; +#endif + namespace Medallion.Threading.MongoDB; /// @@ -14,6 +18,13 @@ public sealed partial class MongoDistributedLock : IInternalDistributedLock + /// ActivitySource for distributed tracing and diagnostics + /// + private static readonly ActivitySource ActivitySource = new("DistributedLock.MongoDB", "1.0.0"); +#endif + // We want to ensure indexes at most once per process per (db, collection) private static readonly ConcurrentDictionary> IndexInitializationTasks = new(StringComparer.Ordinal); @@ -53,6 +64,11 @@ public MongoDistributedLock(string key, IMongoDatabase database, string collecti ValueTask IInternalDistributedLock.InternalTryAcquireAsync(TimeoutValue timeout, CancellationToken cancellationToken) { + if (this._options.UseAdaptiveBackoff) + { + return this.AdaptiveBusyWaitAsync(timeout, cancellationToken); + } + return BusyWaitHelper.WaitAsync(this, (@this, ct) => @this.TryAcquireAsync(ct), timeout, @@ -61,8 +77,77 @@ public MongoDistributedLock(string key, IMongoDatabase database, string collecti cancellationToken); } + private async ValueTask AdaptiveBusyWaitAsync(TimeoutValue timeout, CancellationToken cancellationToken) + { + // Try immediately first + var result = await this.TryAcquireAsync(cancellationToken).ConfigureAwait(false); + if (result != null || timeout.IsZero) + { + return result; + } + + using var timeoutCts = timeout.IsInfinite ? null : new CancellationTokenSource(timeout.TimeSpan); + using var linkedCts = timeoutCts != null + ? CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token) + : null; + var effectiveToken = linkedCts?.Token ?? cancellationToken; + + var minMs = this._options.MinBusyWaitSleepTime.InMilliseconds; + var maxMs = this._options.MaxBusyWaitSleepTime.InMilliseconds; + var consecutiveFailures = 0; + const double BackoffMultiplier = 1.5; +#if NET8_0_OR_GREATER + var random = Random.Shared; +#else + var random = new Random(Guid.NewGuid().GetHashCode()); +#endif + + while (!effectiveToken.IsCancellationRequested) + { + // Exponential backoff with jitter + var backoffMs = minMs * Math.Pow(BackoffMultiplier, Math.Min(consecutiveFailures, 10)); + var sleepMs = Math.Min(backoffMs, maxMs); + // Add jitter (±20%) to prevent thundering herd + var jitter = (random.NextDouble() - 0.5) * 0.4 * sleepMs; + var finalSleepMs = Math.Max(minMs, sleepMs + jitter); + + try + { + await Task.Delay(TimeSpan.FromMilliseconds(finalSleepMs), effectiveToken).ConfigureAwait(false); + } + catch (OperationCanceledException) when (timeoutCts?.IsCancellationRequested == true && !cancellationToken.IsCancellationRequested) + { + // Timeout expired, try one last time + return await this.TryAcquireAsync(cancellationToken).ConfigureAwait(false); + } + + try + { + result = await this.TryAcquireAsync(effectiveToken).ConfigureAwait(false); + if (result != null) + { + return result; + } + consecutiveFailures++; + } + catch (OperationCanceledException) when (timeoutCts?.IsCancellationRequested == true && !cancellationToken.IsCancellationRequested) + { + return null; + } + } + + cancellationToken.ThrowIfCancellationRequested(); + return null; + } + private async ValueTask TryAcquireAsync(CancellationToken cancellationToken) { +#if NET8_0_OR_GREATER + using var activity = ActivitySource.StartActivity("MongoDistributedLock.TryAcquire"); + activity?.SetTag("lock.key", this.Key); + activity?.SetTag("lock.collection", this._collectionName); +#endif + var collection = this._database.GetCollection(this._collectionName); // Ensure indexes exist (TTL cleanup); do this at most once per process per (db, collection) @@ -88,13 +173,21 @@ public MongoDistributedLock(string key, IMongoDatabase database, string collecti // Verify we actually got the lock if (result != null && result.LockId == lockId) { +#if NET8_0_OR_GREATER + activity?.SetTag("lock.acquired", true); + activity?.SetTag("lock.fencing_token", result.FencingToken); +#endif return new(collection, this.Key, lockId, + result.FencingToken, this._options.Expiry, this._options.ExtensionCadence); } +#if NET8_0_OR_GREATER + activity?.SetTag("lock.acquired", false); +#endif return null; } @@ -126,6 +219,16 @@ private static UpdateDefinition CreateAcquireUpdate(string lo } ); + // Increment fencing token only when acquiring a new lock + var newFencingToken = new BsonDocument( + "$add", + new BsonArray + { + new BsonDocument("$ifNull", new BsonArray { "$fencingToken", 0L }), + 1L + } + ); + var setStage = new BsonDocument( "$set", new BsonDocument @@ -133,7 +236,8 @@ private static UpdateDefinition CreateAcquireUpdate(string lo // Only overwrite lock fields when the previous lock is expired/missing { "lockId", new BsonDocument("$cond", new BsonArray { expiredOrMissing, lockId, "$lockId" }) }, { "expiresAt", new BsonDocument("$cond", new BsonArray { expiredOrMissing, newExpiresAt, "$expiresAt" }) }, - { "acquiredAt", new BsonDocument("$cond", new BsonArray { expiredOrMissing, "$$NOW", "$acquiredAt" }) } + { "acquiredAt", new BsonDocument("$cond", new BsonArray { expiredOrMissing, "$$NOW", "$acquiredAt" }) }, + { "fencingToken", new BsonDocument("$cond", new BsonArray { expiredOrMissing, newFencingToken, "$fencingToken" }) } } ); @@ -160,13 +264,21 @@ private static async Task CreateIndexesAsync(IMongoCollection { // TTL cleanup: remove documents once expiresAt < now ExpireAfter = TimeSpan.Zero, + Name = "expiresAt_ttl" }; var indexModel = new CreateIndexModel(indexKeys, indexOptions); await collection.Indexes.CreateOneAsync(indexModel, cancellationToken: CancellationToken.None).ConfigureAwait(false); } - catch + catch (MongoCommandException ex) when ( + ex.CodeName is "IndexOptionsConflict" or "IndexKeySpecsConflict" or "IndexAlreadyExists") + { + // Index already exists with same or different options - this is acceptable. + // The existing index will still handle TTL cleanup. + } + catch (MongoException) { - // Index may already exist, or server may reject options (e.g., conflicts). Ignore. + // Other MongoDB errors (network, auth, etc.) - swallow to avoid blocking lock acquisition. + // The lock will still work correctly; TTL cleanup is a best-effort optimization. } } } \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs b/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs index 2c0b694f..8bc704ba 100644 --- a/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs +++ b/src/DistributedLock.MongoDB/MongoDistributedLockHandle.cs @@ -21,16 +21,24 @@ public sealed class MongoDistributedLockHandle : IDistributedSynchronizationHand /// public CancellationToken HandleLostToken => this._cts.Token; + /// + /// Gets the fencing token for this lock acquisition. This is a monotonically increasing value + /// that can be used to detect stale operations when working with external resources. + /// + public long FencingToken { get; } + internal MongoDistributedLockHandle( IMongoCollection collection, string key, string lockId, + long fencingToken, TimeoutValue expiry, TimeoutValue extensionCadence) { this._collection = collection; this._key = key; this._lockId = lockId; + this.FencingToken = fencingToken; this._cts = new(); // Start background task to extend the lock @@ -101,6 +109,9 @@ public async ValueTask DisposeAsync() private async Task ExtendLockAsync(TimeoutValue expiry, TimeoutValue extensionCadence, CancellationToken cancellationToken) { + const int MaxConsecutiveFailures = 3; + var consecutiveFailures = 0; + try { while (!cancellationToken.IsCancellationRequested) @@ -122,19 +133,43 @@ private async Task ExtendLockAsync(TimeoutValue expiry, TimeoutValue extensionCa new[] { new BsonDocument("$set", new BsonDocument("expiresAt", newExpiresAt)) } ); - var result = await this._collection.UpdateOneAsync(filter, update, cancellationToken: cancellationToken).ConfigureAwait(false); + try + { + var result = await this._collection.UpdateOneAsync(filter, update, cancellationToken: cancellationToken).ConfigureAwait(false); + + // If we successfully extended, reset failure count + if (result.MatchedCount is not 0) + { + consecutiveFailures = 0; + continue; + } - // If we failed to extend, the lock was lost - if (result.MatchedCount is not 0) + // Lock was truly lost (document doesn't exist or lockId changed) + await this.SignalLockLostAsync().ConfigureAwait(false); + break; + } + catch (OperationCanceledException) { - continue; + throw; // Propagate cancellation + } + catch (MongoException) when (++consecutiveFailures < MaxConsecutiveFailures) + { + // Transient network error, retry after a short delay + try + { + await Task.Delay(TimeSpan.FromMilliseconds(100 * consecutiveFailures), cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + throw; + } + } + catch (MongoException) + { + // Too many consecutive failures, assume lock is lost + await this.SignalLockLostAsync().ConfigureAwait(false); + break; } -#if NET8_0_OR_GREATER - await this._cts.CancelAsync().ConfigureAwait(false); -#else - this._cts.Cancel(); -#endif - break; } } catch (OperationCanceledException) @@ -144,12 +179,18 @@ private async Task ExtendLockAsync(TimeoutValue expiry, TimeoutValue extensionCa catch { // Lock extension failed, signal that the lock is lost + await this.SignalLockLostAsync().ConfigureAwait(false); + } + } + + private async Task SignalLockLostAsync() + { #if NET8_0_OR_GREATER - await this._cts.CancelAsync().ConfigureAwait(false); + await this._cts.CancelAsync().ConfigureAwait(false); #else - this._cts.Cancel(); + this._cts.Cancel(); + await Task.CompletedTask.ConfigureAwait(false); #endif - } } private async ValueTask ReleaseLockAsync(CancellationToken cancellationToken) diff --git a/src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs b/src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs index b021030d..5bb25e9b 100644 --- a/src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs +++ b/src/DistributedLock.MongoDB/MongoDistributedSynchronizationOptionsBuilder.cs @@ -15,6 +15,7 @@ public sealed class MongoDistributedSynchronizationOptionsBuilder private static readonly TimeoutValue MinimumExpiry = TimeSpan.FromSeconds(.1); private TimeoutValue? _expiry, _extensionCadence, _minBusyWaitSleepTime, _maxBusyWaitSleepTime; + private bool? _useAdaptiveBackoff; private MongoDistributedSynchronizationOptionsBuilder() { } @@ -73,6 +74,19 @@ public MongoDistributedSynchronizationOptionsBuilder BusyWaitSleepTime(TimeSpan return this; } + /// + /// Enables adaptive exponential backoff for busy waiting. When enabled, the sleep time between + /// acquisition attempts will start at the minimum and exponentially increase towards the maximum + /// as consecutive failures occur, then reset on success. This reduces MongoDB load under high + /// contention while maintaining responsiveness when contention is low. + /// Defaults to false (random sleep time within the configured range). + /// + public MongoDistributedSynchronizationOptionsBuilder UseAdaptiveBackoff(bool enabled = true) + { + _useAdaptiveBackoff = enabled; + return this; + } + internal static MongoDistributedLockOptions GetOptions(Action? optionsBuilder) { MongoDistributedSynchronizationOptionsBuilder? options; @@ -104,7 +118,8 @@ internal static MongoDistributedLockOptions GetOptions(Action + /// Monotonically increasing fencing token for safe resource access + /// + [BsonElement("fencingToken")] + public long FencingToken { get; set; } + /// /// The lock name/key (MongoDB document ID) /// diff --git a/src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt b/src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt index 815c9200..d521649e 100644 --- a/src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt +++ b/src/DistributedLock.MongoDB/PublicAPI.Unshipped.txt @@ -1 +1,3 @@ -#nullable enable \ No newline at end of file +#nullable enable +Medallion.Threading.MongoDB.MongoDistributedLockHandle.FencingToken.get -> long +Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder.UseAdaptiveBackoff(bool enabled = true) -> Medallion.Threading.MongoDB.MongoDistributedSynchronizationOptionsBuilder! \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/packages.lock.json b/src/DistributedLock.MongoDB/packages.lock.json index bfc80002..269aa23d 100644 --- a/src/DistributedLock.MongoDB/packages.lock.json +++ b/src/DistributedLock.MongoDB/packages.lock.json @@ -261,7 +261,6 @@ "MongoDB.Bson": "3.5.2", "SharpCompress": "0.30.1", "Snappier": "1.0.0", - "System.Buffers": "4.5.1", "ZstdSharp.Port": "0.7.3" } }, @@ -293,8 +292,6 @@ "resolved": "5.0.0", "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", "System.Security.AccessControl": "5.0.0", "System.Security.Principal.Windows": "5.0.0" } @@ -304,7 +301,6 @@ "resolved": "3.5.2", "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", "dependencies": { - "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, @@ -324,26 +320,6 @@ "System.Runtime.CompilerServices.Unsafe": "4.7.1" } }, - "System.Buffers": { - "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.Memory": { - "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" - }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", @@ -382,7 +358,7 @@ "type": "Project" } }, - "net8.0": { + "net10.0": { "Microsoft.CodeAnalysis.PublicApiAnalyzers": { "type": "Direct", "requested": "[3.3.4, )", @@ -391,9 +367,9 @@ }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[8.0.22, )", - "resolved": "8.0.22", - "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "ISahzLHsHY7vrwqr2p1YWZ+gsxoBRtH7gWRDK8fDUst9pp2He0GiesaqEfeX0V8QMCJM3eNEHGGpnIcPjFo2NQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", @@ -416,17 +392,13 @@ "MongoDB.Bson": "3.5.2", "SharpCompress": "0.30.1", "Snappier": "1.0.0", - "System.Buffers": "4.5.1", "ZstdSharp.Port": "0.7.3" } }, "DnsClient": { "type": "Transitive", "resolved": "1.6.1", - "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", - "dependencies": { - "Microsoft.Win32.Registry": "5.0.0" - } + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==" }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", @@ -438,33 +410,15 @@ "resolved": "2.0.0", "contentHash": "6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==" }, - "Microsoft.NETCore.Platforms": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" - }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, - "Microsoft.Win32.Registry": { - "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", - "dependencies": { - "System.Security.AccessControl": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } - }, "MongoDB.Bson": { "type": "Transitive", "resolved": "3.5.2", - "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "5.0.0" - } + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==" }, "SharpCompress": { "type": "Transitive", @@ -476,34 +430,86 @@ "resolved": "1.0.0", "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" }, - "System.Buffers": { + "ZstdSharp.Port": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "0.7.3", + "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" }, - "System.Memory": { + "distributedlock.core": { + "type": "Project" + } + }, + "net8.0": { + "Microsoft.CodeAnalysis.PublicApiAnalyzers": { + "type": "Direct", + "requested": "[3.3.4, )", + "resolved": "3.3.4", + "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" + }, + "Microsoft.NET.ILLink.Tasks": { + "type": "Direct", + "requested": "[8.0.22, )", + "resolved": "8.0.22", + "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" + }, + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "MongoDB.Driver": { + "type": "Direct", + "requested": "[3.5.2, )", + "resolved": "3.5.2", + "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", + "dependencies": { + "DnsClient": "1.6.1", + "Microsoft.Extensions.Logging.Abstractions": "2.0.0", + "MongoDB.Bson": "3.5.2", + "SharpCompress": "0.30.1", + "Snappier": "1.0.0", + "ZstdSharp.Port": "0.7.3" + } + }, + "DnsClient": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" + "resolved": "1.6.1", + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==" }, - "System.Runtime.CompilerServices.Unsafe": { + "Microsoft.Build.Tasks.Git": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" }, - "System.Security.AccessControl": { + "Microsoft.Extensions.Logging.Abstractions": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", - "dependencies": { - "Microsoft.NETCore.Platforms": "5.0.0", - "System.Security.Principal.Windows": "5.0.0" - } + "resolved": "2.0.0", + "contentHash": "6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==" }, - "System.Security.Principal.Windows": { + "Microsoft.SourceLink.Common": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + }, + "MongoDB.Bson": { + "type": "Transitive", + "resolved": "3.5.2", + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==" + }, + "SharpCompress": { + "type": "Transitive", + "resolved": "0.30.1", + "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + }, + "Snappier": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" }, "ZstdSharp.Port": { "type": "Transitive", From 14b924d19828f1c134f10e77f077db17e9b9c08d Mon Sep 17 00:00:00 2001 From: Joes Date: Mon, 5 Jan 2026 14:18:12 +0800 Subject: [PATCH 15/21] remove dotnet 10 the current ci does not support. --- .../DistributedLock.MongoDB.csproj | 2 +- .../packages.lock.json | 148 +++++++++--------- 2 files changed, 72 insertions(+), 78 deletions(-) diff --git a/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj b/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj index 07428793..7259e5a4 100644 --- a/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj +++ b/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj @@ -1,7 +1,7 @@ - netstandard2.1;net8.0;net10.0;net472; + netstandard2.1;net8.0;net472; Medallion.Threading.MongoDB True 4 diff --git a/src/DistributedLock.MongoDB/packages.lock.json b/src/DistributedLock.MongoDB/packages.lock.json index 269aa23d..bfc80002 100644 --- a/src/DistributedLock.MongoDB/packages.lock.json +++ b/src/DistributedLock.MongoDB/packages.lock.json @@ -261,6 +261,7 @@ "MongoDB.Bson": "3.5.2", "SharpCompress": "0.30.1", "Snappier": "1.0.0", + "System.Buffers": "4.5.1", "ZstdSharp.Port": "0.7.3" } }, @@ -292,6 +293,8 @@ "resolved": "5.0.0", "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", "dependencies": { + "System.Buffers": "4.5.1", + "System.Memory": "4.5.4", "System.Security.AccessControl": "5.0.0", "System.Security.Principal.Windows": "5.0.0" } @@ -301,6 +304,7 @@ "resolved": "3.5.2", "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", "dependencies": { + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "5.0.0" } }, @@ -320,6 +324,26 @@ "System.Runtime.CompilerServices.Unsafe": "4.7.1" } }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", @@ -358,7 +382,7 @@ "type": "Project" } }, - "net10.0": { + "net8.0": { "Microsoft.CodeAnalysis.PublicApiAnalyzers": { "type": "Direct", "requested": "[3.3.4, )", @@ -367,9 +391,9 @@ }, "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "ISahzLHsHY7vrwqr2p1YWZ+gsxoBRtH7gWRDK8fDUst9pp2He0GiesaqEfeX0V8QMCJM3eNEHGGpnIcPjFo2NQ==" + "requested": "[8.0.22, )", + "resolved": "8.0.22", + "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" }, "Microsoft.SourceLink.GitHub": { "type": "Direct", @@ -392,13 +416,17 @@ "MongoDB.Bson": "3.5.2", "SharpCompress": "0.30.1", "Snappier": "1.0.0", + "System.Buffers": "4.5.1", "ZstdSharp.Port": "0.7.3" } }, "DnsClient": { "type": "Transitive", "resolved": "1.6.1", - "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==" + "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==", + "dependencies": { + "Microsoft.Win32.Registry": "5.0.0" + } }, "Microsoft.Build.Tasks.Git": { "type": "Transitive", @@ -410,15 +438,33 @@ "resolved": "2.0.0", "contentHash": "6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==" }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==" + }, "Microsoft.SourceLink.Common": { "type": "Transitive", "resolved": "8.0.0", "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==", + "dependencies": { + "System.Security.AccessControl": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } + }, "MongoDB.Bson": { "type": "Transitive", "resolved": "3.5.2", - "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==" + "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "5.0.0" + } }, "SharpCompress": { "type": "Transitive", @@ -430,86 +476,34 @@ "resolved": "1.0.0", "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" }, - "ZstdSharp.Port": { - "type": "Transitive", - "resolved": "0.7.3", - "contentHash": "U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==" - }, - "distributedlock.core": { - "type": "Project" - } - }, - "net8.0": { - "Microsoft.CodeAnalysis.PublicApiAnalyzers": { - "type": "Direct", - "requested": "[3.3.4, )", - "resolved": "3.3.4", - "contentHash": "kNLTfXtXUWDHVt5iaPkkiPuyHYlMgLI6SOFT4w88bfeI2vqSeGgHunFkdvlaCM8RDfcY0t2+jnesQtidRJJ/DA==" - }, - "Microsoft.NET.ILLink.Tasks": { - "type": "Direct", - "requested": "[8.0.22, )", - "resolved": "8.0.22", - "contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ==" - }, - "Microsoft.SourceLink.GitHub": { - "type": "Direct", - "requested": "[8.0.0, )", - "resolved": "8.0.0", - "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", - "dependencies": { - "Microsoft.Build.Tasks.Git": "8.0.0", - "Microsoft.SourceLink.Common": "8.0.0" - } - }, - "MongoDB.Driver": { - "type": "Direct", - "requested": "[3.5.2, )", - "resolved": "3.5.2", - "contentHash": "Z1EW+CI18wOQqyAnhZkQXvUVXycrHoOb8/dlJzPHKFfNp9XPB6sEJJR67GUVYyB03B5xzt1RlwSyLXAx9B5+yQ==", - "dependencies": { - "DnsClient": "1.6.1", - "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "MongoDB.Bson": "3.5.2", - "SharpCompress": "0.30.1", - "Snappier": "1.0.0", - "ZstdSharp.Port": "0.7.3" - } - }, - "DnsClient": { - "type": "Transitive", - "resolved": "1.6.1", - "contentHash": "4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==" - }, - "Microsoft.Build.Tasks.Git": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" - }, - "Microsoft.Extensions.Logging.Abstractions": { + "System.Buffers": { "type": "Transitive", - "resolved": "2.0.0", - "contentHash": "6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==" + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "Microsoft.SourceLink.Common": { + "System.Memory": { "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, - "MongoDB.Bson": { + "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "3.5.2", - "contentHash": "aTlbKclBjXi4j3AkrHiaeuKCvdoGA029VbvXJUmmsCXDz+DEfBFq6n9uOvXJu2YXcJiW9Vay1ZctY5Iu0JRXdw==" + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" }, - "SharpCompress": { + "System.Security.AccessControl": { "type": "Transitive", - "resolved": "0.30.1", - "contentHash": "XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==" + "resolved": "5.0.0", + "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "5.0.0", + "System.Security.Principal.Windows": "5.0.0" + } }, - "Snappier": { + "System.Security.Principal.Windows": { "type": "Transitive", - "resolved": "1.0.0", - "contentHash": "rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==" + "resolved": "5.0.0", + "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==" }, "ZstdSharp.Port": { "type": "Transitive", From 55c081a3c222cf927286e5c30abb1bc877b5ca44 Mon Sep 17 00:00:00 2001 From: Joes Date: Mon, 5 Jan 2026 15:43:22 +0800 Subject: [PATCH 16/21] feat: update docs --- src/DistributedLock.MongoDB/README.md | 414 ++++++++++++++++- src/DistributedLock.MongoDB/README.zh-CN.md | 478 ++++++++++++++++++++ 2 files changed, 871 insertions(+), 21 deletions(-) create mode 100644 src/DistributedLock.MongoDB/README.zh-CN.md diff --git a/src/DistributedLock.MongoDB/README.md b/src/DistributedLock.MongoDB/README.md index 2cac9f0d..6a9ecc26 100644 --- a/src/DistributedLock.MongoDB/README.md +++ b/src/DistributedLock.MongoDB/README.md @@ -1,6 +1,6 @@ # DistributedLock.MongoDB -This library provides distributed lock implementation using MongoDB as the backing store. +Provides distributed lock implementation using MongoDB as the backing store. ## Installation @@ -8,9 +8,14 @@ This library provides distributed lock implementation using MongoDB as the backi dotnet add package DistributedLock.MongoDB ``` -## Usage +## APIs -### Basic Lock Usage +- The `MongoDistributedLock` class implements the `IDistributedLock` interface. +- The `MongoDistributedSynchronizationProvider` class implements the `IDistributedLockProvider` interface. + +## Basic Usage + +### Creating and Acquiring a Lock ```csharp using Medallion.Threading.MongoDB; @@ -32,7 +37,27 @@ await using (var handle = await @lock.AcquireAsync()) // Lock is automatically released when disposed ``` -### Using the Provider +### TryAcquire + +While `AcquireAsync()` will block until the lock is available, there is also a `TryAcquireAsync()` variant which returns `null` if the lock could not be acquired: + +```csharp +await using (var handle = await @lock.TryAcquireAsync()) +{ + if (handle != null) + { + // Lock acquired successfully + } + else + { + // Someone else holds the lock + } +} +``` + +### Using the Provider Pattern + +For applications using dependency injection, the provider pattern makes it easy to separate lock configuration from its usage: ```csharp using Medallion.Threading.MongoDB; @@ -60,43 +85,390 @@ You can customize the lock behavior using the options builder: ```csharp var @lock = new MongoDistributedLock( - "myLockName", + "myLockName", database, options => options - .Expiry(TimeSpan.FromSeconds(30)) // Lock expiry time - .ExtensionCadence(TimeSpan.FromSeconds(10)) // How often to extend the lock - .BusyWaitSleepTime( // Sleep time between acquire attempts + .Expiry(TimeSpan.FromSeconds(30)) // How long the lock lasts before expiring + .ExtensionCadence(TimeSpan.FromSeconds(10)) // How often to extend the lock while held + .BusyWaitSleepTime( // Sleep time between acquire attempts min: TimeSpan.FromMilliseconds(10), max: TimeSpan.FromMilliseconds(800)) + .UseAdaptiveBackoff(true) // Exponential backoff under high contention ); ``` +#### Option Details + +- **Expiry**: Determines how long the lock will be held before automatic expiration. Because of automatic extension, locks can be held for much longer than this value. Defaults to 30 seconds. This is important for crash recovery—if a process crashes while holding a lock, other processes will need to wait at most this long to acquire it. + +- **ExtensionCadence**: How frequently the hold on the lock will be renewed to the full `Expiry` while the lock is actively held. Defaults to 1/3 of `Expiry` (approximately 10 seconds with default settings). + +- **BusyWaitSleepTime**: Specifies the range of time the implementation will sleep between attempts to acquire a lock that is currently held by another process. A random value in the range is chosen for each attempt to prevent thundering herd problems. Defaults to 10ms minimum and 800ms maximum. + +- **UseAdaptiveBackoff**: When enabled, uses exponential backoff instead of random sleep times under contention. This reduces MongoDB load during high contention periods while maintaining responsiveness when contention is low. Defaults to false. + ### Custom Collection Name -By default, locks are stored in a collection named "DistributedLocks". You can specify a custom collection name: +By default, locks are stored in a collection named `"DistributedLocks"`. You can specify a custom collection name: ```csharp +// Direct lock creation with custom collection var @lock = new MongoDistributedLock("myLockName", database, "MyCustomLocks"); + +// Provider with custom collection +var provider = new MongoDistributedSynchronizationProvider(database, "MyCustomLocks"); +``` + +## Architecture & Design + +### Lock Lifecycle Diagram + +```mermaid +stateDiagram-v2 + [*] --> Waiting: Create Lock + + Waiting --> Acquiring: Call AcquireAsync() + Waiting --> Expired: TTL Cleanup + + Acquiring --> Acquired: Successfully Acquired Lock + Acquiring --> Waiting: Lock Held by Others + + Acquired --> Extending: Background Extension Task + Extending --> Acquired: Extension Successful + Extending --> Lost: Extension Failed + + Acquired --> Releasing: Dispose Handle + Releasing --> Released: Lock Document Deleted + + Lost --> [*]: HandleLostToken Signaled + Released --> [*]: Lock Released + Expired --> [*]: Stale Lock Cleaned Up +``` + +### Lock Acquisition Process + +```mermaid +flowchart TD + A["Client Requests Lock"] --> B["Call FindOneAndUpdateAsync"] + B --> C{"Lock Document Exists?"} + + C -->|Yes| D{"Document Expired?"} + C -->|No| E["Create New Lock"] + + D -->|Yes| F["Replace Old Lock"] + D -->|No| G["Lock Held by Other Process"] + + E --> H["Set LockId = GUID"] + F --> H + + H --> I["Set expiresAt = Now + Expiry"] + I --> J["Set acquiredAt = Now"] + J --> K["Increment fencingToken"] + K --> L["Verify Our LockId"] + + L --> M{"LockId Matches?"} + M -->|Yes| N["✅ Lock Acquired"] + M -->|No| O["❌ Another Process Won Race"] + + G --> P["Sleep Random Time"] + O --> P + P --> Q["Retry Acquire"] + Q --> B + + N --> R["Start Background Extension Task"] + R --> S["Return LockHandle"] +``` + +### Component Architecture + +```mermaid +graph TB + Client["Client Code"] + Lock["MongoDistributedLock"] + Provider["MongoDistributedSynchronizationProvider"] + Options["MongoDistributedSynchronizationOptionsBuilder"] + Handle["MongoDistributedLockHandle"] + + MongoDB[("MongoDB Database")] + Collection["DistributedLocks Collection"] + TTLIndex["TTL Index on expiresAt"] + + Client -->|Creates| Lock + Client -->|Uses| Provider + Provider -->|Creates| Lock + Lock -->|Configured by| Options + Lock -->|Returns| Handle + Handle -->|Manages| Lock + + Lock -->|Reads/Writes| MongoDB + Handle -->|Auto-extends via| Lock + + MongoDB -->|Contains| Collection + Collection -->|Has| TTLIndex + TTLIndex -->|Cleans up| Collection + + style MongoDB fill:#13aa52 + style Collection fill:#3fa796 + style TTLIndex fill:#5fbf8c + style Handle fill:#ffa500 + style Lock fill:#4a90e2 +``` + +### Lock State Machine (Single Document in MongoDB) + +```mermaid +graph LR + NonExistent["Document Doesn't Exist"] + Active["Active Lock by Process A"] + Extending["Auto-Extending by Process A"] + Expired["Expired (expiresAt < now)"] + Deleted["Deleted"] + + NonExistent -->|Acquire Attempt| Active + Active -->|Background Task| Extending + Extending -->|Success| Active + Extending -->|Failure| Expired + Active -->|Release| Deleted + Expired -->|Other Process Acquires| Active + Expired -->|TTL Cleanup| Deleted + + style Active fill:#90EE90 + style Extending fill:#87CEEB + style Expired fill:#FFB6C1 + style Deleted fill:#D3D3D3 ``` ## How It Works -The MongoDB distributed lock uses MongoDB's document upsert and update operations to implement distributed locking: +MongoDB distributed locks use MongoDB's atomic document operations to implement safe, distributed locking: + +### Acquisition Algorithm + +The lock acquisition uses a single `FindOneAndUpdateAsync` operation with an aggregation pipeline to atomically: + +1. Check if the lock document exists and is expired +2. If expired or missing, acquire the lock by: + - Setting a unique `lockId` (GUID) + - Recording the `acquiredAt` timestamp + - Setting the `expiresAt` time based on the configured expiry + - Incrementing the `fencingToken` for ordering guarantees +3. If still held by another process, leave it unchanged + +The fencing token ensures that even if a lock holder loses its connection, any operations it performs using that token will be safely rejected. + +### Lock Maintenance -1. **Acquisition**: Attempts to insert or update a document with the lock key and a unique lock ID -2. **Extension**: Automatically extends the lock expiry while held to prevent timeout -3. **Release**: Deletes the lock document when disposed -4. **Expiry**: Locks automatically expire if not extended, allowing recovery from crashed processes +Once acquired, the lock is automatically extended in the background at the configured `ExtensionCadence` to prevent premature expiration while the process is still running. + +### Release + +The lock is released by deleting the lock document when the handle is disposed. + +### Stale Lock Cleanup + +A TTL (Time-To-Live) index on the `expiresAt` field ensures MongoDB automatically removes expired lock documents. This provides automatic cleanup of stale locks from crashed or disconnected processes without requiring manual intervention. ## Features -- ✅ Async/await support -- ✅ Automatic lock extension while held -- ✅ Configurable expiry and extension cadence -- ✅ Lock abandonment protection via expiry -- ✅ `CancellationToken` support -- ✅ Handle lost token notification -- ✅ Multi-target support: .NET 8, .NET Standard 2.1, .NET Framework 4.7.2 +- ✅ Async/await support with `async`/`await` and `await using` +- ✅ Automatic lock extension while held to prevent premature expiration +- ✅ Configurable expiry, extension cadence, and wait behavior +- ✅ Crash recovery via automatic lock expiration +- ✅ `CancellationToken` support for cooperative cancellation +- ✅ `HandleLostToken` notifications for detecting connection loss or lock theft +- ✅ Fencing tokens for distributed safety guarantees +- ✅ Adaptive backoff strategy for high-contention scenarios +- ✅ Atomic operations prevent race conditions +- ✅ Multi-framework support: .NET Standard 2.1, .NET 8, .NET Framework 4.7.2 + +## Multi-Process Lock Interaction + +The following diagram shows how multiple processes interact with the same lock: + +```mermaid +sequenceDiagram + participant PA as Process A + participant PB as Process B + participant DB as MongoDB + + PA->>DB: FindOneAndUpdateAsync (try acquire) + Note over PA,DB: expiresAt < now or missing + DB->>PA: ✅ Lock Acquired (lockId=UUID-A, token=1) + + PB->>DB: FindOneAndUpdateAsync (try acquire) + Note over PB,DB: expiresAt > now (held by A) + DB->>PB: ❌ Lock Not Acquired + + PB->>PB: Wait & Retry (exponential backoff) + + PA->>PA: Background: ExtensionCadence timer + PA->>DB: Update: Extend expiresAt += Expiry + DB->>PA: ✅ Extension Successful + + PA->>PA: Do Critical Work + + PB->>DB: FindOneAndUpdateAsync (retry) + DB->>PB: ❌ Still held by A + PB->>PB: Wait & Retry + + PA->>PA: Dispose Handle + PA->>DB: Delete Lock Document + DB->>PA: ✅ Deleted + + PB->>DB: FindOneAndUpdateAsync (retry) + Note over PB,DB: Document missing (A released) + DB->>PB: ✅ Lock Acquired (lockId=UUID-B, token=2) + PB->>PB: Do Critical Work +``` + +## Implementation Behaviors + +### Lock Extension Failures + +Lock extension happens automatically in the background at the specified `ExtensionCadence`. If extension fails (due to network issues, MongoDB connectivity problems, or the lock being stolen), the `HandleLostToken` will be signaled. Always monitor this token in critical sections: + +```csharp +await using (var handle = await @lock.AcquireAsync()) +{ + // Monitor for lock loss + var lostTokenTask = handle.HandleLostToken; + + // Do work with timeout + var completedTask = await Task.WhenAny( + lostTokenTask, + DoWorkAsync()); + + if (completedTask == lostTokenTask) + { + // Lock was lost! + throw new InvalidOperationException("Lock was lost during operation"); + } +} +``` + +### Lock Collection Management + +The lock collection is automatically created on first use. A TTL index is created on the `expiresAt` field with the configuration: + +- **Index Name**: `expiresAt_ttl` +- **Behavior**: MongoDB automatically removes documents once `expiresAt < now` + +This index is created at most once per process per (database, collection) pair. Errors during index creation do not block lock acquisition. + +### Timeout Support + +All acquire methods support optional timeout parameters: + +```csharp +// Acquire with 5-second timeout +var handle = await @lock.AcquireAsync(TimeSpan.FromSeconds(5)); + +// TryAcquire with 5-second timeout (returns null on timeout) +var handle = await @lock.TryAcquireAsync(TimeSpan.FromSeconds(5)); +``` + +## Safety Guarantees + +- **Atomicity**: Lock acquisition, extension, and release use atomic MongoDB operations +- **Fencing**: Fencing tokens prevent operations from processes that lost their locks +- **Crash Recovery**: Automatic expiration ensures locks are eventually released even if the holder crashes +- **No Thundering Herd**: Random and adaptive backoff prevents synchronized clients from overwhelming the database + +### Fencing Token Mechanism + +The fencing token ensures that even if a process loses its lock (due to network partition, crash, or timeout), it cannot perform operations on protected resources: + +```mermaid +sequenceDiagram + participant PA as Process A + participant Resource as Protected Resource + participant DB as MongoDB Lock + + PA->>DB: Acquire Lock → token=1 + DB->>PA: ✅ Granted + + PA->>Resource: Operation with token=1 + Resource->>Resource: Accept (token >= last_seen) + + Note over DB: Network Partition + + PB->>DB: Acquire Lock → token=2 + DB->>PB: ✅ Granted + + PB->>Resource: Operation with token=2 + Resource->>Resource: Accept (token >= last_seen) + + PA->>Resource: Operation with token=1 (stale) + Resource->>Resource: Reject ❌ (token < last_seen) +``` + +This mechanism prevents the "split brain" scenario where two processes both believe they hold the lock. + +## Performance Considerations + +- Lock acquisition requires 1-2 MongoDB operations (1 for acquisition, potentially 1 for index creation on first use) +- Lock extension happens in the background at `ExtensionCadence` intervals +- Under contention, adaptive backoff reduces the load on MongoDB compared to fixed random intervals +- The `expiresAt` TTL index keeps the collection clean without manual maintenance + +## Adaptive Backoff Strategy + +When contention is high, the adaptive backoff strategy exponentially increases the wait time between acquisition attempts: + +```mermaid +graph TD + A["Attempt to Acquire"] --> B{"Lock Available?"} + B -->|Yes| C["✅ Acquired"] + B -->|No| D["Consecutive Failures: N"] + + D --> E["Sleep Time = min * 1.5^N"] + E --> F["Add Random Jitter ±20%"] + F --> G["Wait: max(min, sleepTime + jitter)"] + G --> H["Retry"] + H --> A + + C --> I["Reset Failure Counter"] + + style C fill:#90EE90 + style E fill:#FFD700 + style F fill:#FFA500 +``` + +### Backoff Comparison Chart + +#### Low Contention Scenario + +| Attempt # | Strategy | Sleep Duration | Notes | +| --------- | -------- | -------------- | ------------------ | +| 1st | Random | 234ms | Unpredictable | +| 2nd | Random | 567ms | Unpredictable | +| 3rd | Random | 45ms | Unpredictable | +| 4th | Random | 689ms | Unpredictable | +| 1st | Adaptive | 10ms | Responsive | +| 2nd | Adaptive | 15ms | Exponential growth | +| 3rd | Adaptive | 22ms | Controlled backoff | +| 4th | Adaptive | 33ms | Still responsive | + +#### High Contention Scenario + +| Attempt # | Strategy | Sleep Duration | Impact | +| --------- | -------- | -------------- | -------------------------- | +| 1st | Random | 234ms | Constant high load | +| 2nd | Random | 567ms | Constant high load | +| 3rd | Random | 45ms | Constant high load | +| 4th | Random | 689ms | Constant high load | +| 1st | Adaptive | 10ms | Starts responsive | +| 2nd | Adaptive | 15ms | Progressive backoff | +| 3rd | Adaptive | 22ms | Reduces MongoDB load | +| 4th | Adaptive | 150ms | Significantly reduces load | + +**Key Advantage**: Adaptive backoff automatically adjusts to contention level, providing better resource utilization + +## Framework Support + +- **.NET Standard 2.1** and higher +- **.NET 8** and higher +- **.NET Framework 4.7.2** and higher ## Notes diff --git a/src/DistributedLock.MongoDB/README.zh-CN.md b/src/DistributedLock.MongoDB/README.zh-CN.md new file mode 100644 index 00000000..bb892c84 --- /dev/null +++ b/src/DistributedLock.MongoDB/README.zh-CN.md @@ -0,0 +1,478 @@ +# DistributedLock.MongoDB + +基于 MongoDB 的分布式锁实现。 + +## 安装 + +```bash +dotnet add package DistributedLock.MongoDB +``` + +## 接口 + +- `MongoDistributedLock` 类实现了 `IDistributedLock` 接口。 +- `MongoDistributedSynchronizationProvider` 类实现了 `IDistributedLockProvider` 接口。 + +## 基本用法 + +### 创建并获取锁 + +```csharp +using Medallion.Threading.MongoDB; +using MongoDB.Driver; + +// 创建 MongoDB 客户端和数据库 +var client = new MongoClient("mongodb://localhost:27017"); +var database = client.GetDatabase("myDatabase"); + +// 创建锁 +var @lock = new MongoDistributedLock("myLockName", database); + +// 获取锁 +await using (var handle = await @lock.AcquireAsync()) +{ + // 受锁保护的关键代码段 + Console.WriteLine("已获取锁!"); +} +// 释放锁 +``` + +### TryAcquire(尝试获取) + +`AcquireAsync()` 会一直阻塞直到获取锁,而 `TryAcquireAsync()` 则会在无法获取锁时返回 `null`: + +```csharp +await using (var handle = await @lock.TryAcquireAsync()) +{ + if (handle != null) + { + // 成功获取锁 + } + else + { + // 其他进程持有锁 + } +} +``` + +### 使用提供者模式 + +对于使用依赖注入的应用,提供者模式使得锁配置与使用分离变得容易: + +```csharp +using Medallion.Threading.MongoDB; +using MongoDB.Driver; + +var client = new MongoClient("mongodb://localhost:27017"); +var database = client.GetDatabase("myDatabase"); + +// 创建提供者 +var provider = new MongoDistributedSynchronizationProvider(database); + +// 使用提供者创建不同名称的锁 +var lock1 = provider.CreateLock("lock1"); +var lock2 = provider.CreateLock("lock2"); + +await using (var handle = await lock1.AcquireAsync()) +{ + // 使用 lock1 进行工作... +} +``` + +### 配置选项 + +您可以使用选项构建器自定义锁的行为: + +```csharp +var @lock = new MongoDistributedLock( + "myLockName", + database, + options => options + .Expiry(TimeSpan.FromSeconds(30)) // 锁过期前的持续时间 + .ExtensionCadence(TimeSpan.FromSeconds(10)) // 持有时自动续期的频率 + .BusyWaitSleepTime( // 获取尝试之间的睡眠时间 + min: TimeSpan.FromMilliseconds(10), + max: TimeSpan.FromMilliseconds(800)) + .UseAdaptiveBackoff(true) // 高竞争下的指数退避 +); +``` + +#### 选项详解 + +- **Expiry(过期时间)**:确定锁在自动过期前将被持有多长时间。由于自动续期的存在,锁可以被持有远比这个值更长的时间。默认为 30 秒。这对于崩溃恢复很重要——如果进程在持有锁时崩溃,其他进程最多需要等待这么长时间才能获取锁。 + +- **ExtensionCadence(续期节奏)**:当锁被主动持有时,将锁的保持时间续期到完整的 `Expiry` 的频率。默认为 `Expiry` 的 1/3(使用默认设置时约 10 秒)。 + +- **BusyWaitSleepTime(忙等待睡眠时间)**:指定实现在尝试获取当前由另一个进程持有的锁时,将在两次尝试之间睡眠的时间范围。每次尝试都会从范围内随机选择一个值,以防止"羊群效应"问题。默认最小 10ms,最大 800ms。 + +- **UseAdaptiveBackoff(自适应退避)**:启用后,在竞争时使用指数退避而非随机睡眠时间。这在高竞争期间减少了 MongoDB 负载,同时在竞争低时保持了响应性。默认为 false。 + +### 自定义集合名称 + +默认情况下,锁存储在名为 `"DistributedLocks"` 的集合中。您可以指定自定义集合名称: + +```csharp +// 直接创建锁并使用自定义集合 +var @lock = new MongoDistributedLock("myLockName", database, "MyCustomLocks"); + +// 使用自定义集合的提供者 +var provider = new MongoDistributedSynchronizationProvider(database, "MyCustomLocks"); +``` + +## 架构与设计 + +### 锁生命周期图 + +```mermaid +stateDiagram-v2 + [*] --> 等待中: 创建锁 + + 等待中 --> 获取中: 调用 AcquireAsync() + 等待中 --> 已过期: TTL 清理 + + 获取中 --> 已获取: 成功获取锁 + 获取中 --> 等待中: 锁被其他进程持有 + + 已获取 --> 续期中: 后台续期任务 + 续期中 --> 已获取: 续期成功 + 续期中 --> 丢失: 续期失败 + + 已获取 --> 释放中: 释放 Handle + 释放中 --> 已释放: 锁文档被删除 + + 丢失 --> [*]: HandleLostToken 信号 + 已释放 --> [*]: 锁已释放 + 已过期 --> [*]: 陈旧锁被清理 +``` + +### 锁获取流程 + +```mermaid +flowchart TD + A["客户端请求锁"] --> B["调用 FindOneAndUpdateAsync"] + B --> C{"锁文档存在?"} + + C -->|是| D{"文档已过期?"} + C -->|否| E["创建新锁"] + + D -->|是| F["替换旧锁"] + D -->|否| G["锁由其他进程持有"] + + E --> H["设置 LockId = GUID"] + F --> H + + H --> I["设置 expiresAt = Now + Expiry"] + I --> J["设置 acquiredAt = Now"] + J --> K["递增 fencingToken"] + K --> L["验证 LockId"] + + L --> M{"LockId匹配?"} + M -->|是| N["✅ 锁已获取"] + M -->|否| O["❌ 其他进程赢得竞争"] + + G --> P["随机睡眠"] + O --> P + P --> Q["重试获取"] + Q --> B + + N --> R["启动后台续期任务"] + R --> S["返回 LockHandle"] +``` + +### 组件架构 + +```mermaid +graph TB + Client["客户端代码"] + Lock["MongoDistributedLock"] + Provider["MongoDistributedSynchronizationProvider"] + Options["MongoDistributedSynchronizationOptionsBuilder"] + Handle["MongoDistributedLockHandle"] + + MongoDB[("MongoDB 数据库")] + Collection["DistributedLocks 集合"] + TTLIndex["expiresAt 上的 TTL 索引"] + + Client -->|创建| Lock + Client -->|使用| Provider + Provider -->|创建| Lock + Lock -->|配置| Options + Lock -->|返回| Handle + Handle -->|管理| Lock + + Lock -->|读/写| MongoDB + Handle -->|自动续期| Lock + + MongoDB -->|包含| Collection + Collection -->|拥有| TTLIndex + TTLIndex -->|清理| Collection + + style MongoDB fill:#13aa52 + style Collection fill:#3fa796 + style TTLIndex fill:#5fbf8c + style Handle fill:#ffa500 + style Lock fill:#4a90e2 +``` + +### 锁状态机(MongoDB 中的单个文档) + +```mermaid +graph LR + NonExistent["文档不存在"] + Active["活跃锁由进程 A 持有"] + Extending["自动续期由进程 A"] + Expired["已过期(expiresAt < now)"] + Deleted["已删除"] + + NonExistent -->|获取尝试| Active + Active -->|后台任务| Extending + Extending -->|成功| Active + Extending -->|失败| Expired + Active -->|释放| Deleted + Expired -->|其他进程获取| Active + Expired -->|TTL 清理| Deleted + + style Active fill:#90EE90 + style Extending fill:#87CEEB + style Expired fill:#FFB6C1 + style Deleted fill:#D3D3D3 +``` + +## 工作原理 + +MongoDB 分布式锁使用 MongoDB 的原子文档操作来实现安全的分布式锁定: + +### 获取算法 + +锁获取使用单个 `FindOneAndUpdateAsync` 操作和聚合管道来原子地: + +1. 检查锁文档是否存在且是否已过期 +2. 如果已过期或不存在,通过以下方式获取锁: + - 设置唯一的 `lockId`(GUID) + - 记录 `acquiredAt` 时间戳 + - 根据配置的过期时间设置 `expiresAt` + - 递增 `fencingToken` 以获得排序保证 +3. 如果仍由另一个进程持有,则保持不变 + +围栏令牌(fencing token)确保即使锁持有者失去连接,它执行的任何使用该令牌的操作都会被安全拒绝。 + +### 锁维护 + +获取后,锁会按照配置的 `ExtensionCadence` 在后台自动续期,以防止进程运行时过早过期。 + +### 释放 + +当处理被释放时,通过删除锁文档来释放锁。 + +### 陈旧锁清理 + +`expiresAt` 字段上的 TTL(生存时间)索引确保 MongoDB 自动删除已过期的锁文档。这提供了对来自崩溃或断开连接进程的陈旧锁的自动清理,无需手动干预。 + +## 特性 + +- ✅ 异步/等待支持(`async`/`await` 和 `await using`) +- ✅ 在持有时自动续期锁以防止过早过期 +- ✅ 可配置的过期时间、续期节奏和等待行为 +- ✅ 通过自动过期实现崩溃恢复 +- ✅ `CancellationToken` 支持协作取消 +- ✅ `HandleLostToken` 通知以检测连接丢失或锁被盗 +- ✅ 围栏令牌提供分布式安全保证 +- ✅ 高竞争场景的自适应退避策略 +- ✅ 原子操作防止竞态条件 +- ✅ 多框架支持:.NET Standard 2.1、.NET 8、.NET Framework 4.7.2 + +## 多进程锁交互 + +下图展示多个进程如何与同一个锁交互: + +```mermaid +sequenceDiagram + participant PA as 进程 A + participant PB as 进程 B + participant DB as MongoDB + + PA->>DB: FindOneAndUpdateAsync (尝试获取) + Note over PA,DB: expiresAt < now 或不存在 + DB->>PA: ✅ 锁已获取 (lockId=UUID-A, token=1) + + PB->>DB: FindOneAndUpdateAsync (尝试获取) + Note over PB,DB: expiresAt > now (由 A 持有) + DB->>PB: ❌ 未能获取锁 + + PB->>PB: 等待 & 重试 (指数退避) + + PA->>PA: 后台: ExtensionCadence 计时器 + PA->>DB: 更新: 续期 expiresAt += Expiry + DB->>PA: ✅ 续期成功 + + PA->>PA: 执行关键工作 + + PB->>DB: FindOneAndUpdateAsync (重试) + DB->>PB: ❌ 仍由 A 持有 + PB->>PB: 等待 & 重试 + + PA->>PA: 释放 Handle + PA->>DB: 删除锁文档 + DB->>PA: ✅ 已删除 + + PB->>DB: FindOneAndUpdateAsync (重试) + Note over PB,DB: 文档缺失 (A 已释放) + DB->>PB: ✅ 锁已获取 (lockId=UUID-B, token=2) + PB->>PB: 执行关键工作 +``` + +## 实现行为 + +### 锁续期失败 + +锁续期在后台按指定的 `ExtensionCadence` 自动进行。如果续期失败(由于网络问题、MongoDB 连接问题或锁被盗),`HandleLostToken` 将被触发。始终在关键代码段中监控此令牌: + +```csharp +await using (var handle = await @lock.AcquireAsync()) +{ + // 监控锁丢失 + var lostTokenTask = handle.HandleLostToken; + + // 进行工作,带有超时 + var completedTask = await Task.WhenAny( + lostTokenTask, + DoWorkAsync()); + + if (completedTask == lostTokenTask) + { + // 锁丢失了! + throw new InvalidOperationException("操作期间锁被丢失"); + } +} +``` + +### 锁集合管理 + +锁集合在首次使用时自动创建。在 `expiresAt` 字段上创建 TTL 索引,配置如下: + +- **索引名称**:`expiresAt_ttl` +- **行为**:一旦 `expiresAt < now`,MongoDB 自动删除文档 + +此索引每个进程每个 (数据库, 集合) 对最多创建一次。索引创建期间的错误不会阻止锁获取。 + +### 超时支持 + +所有获取方法都支持可选的超时参数: + +```csharp +// 获取锁并设置 5 秒超时 +var handle = await @lock.AcquireAsync(TimeSpan.FromSeconds(5)); + +// TryAcquire 设置 5 秒超时(超时时返回 null) +var handle = await @lock.TryAcquireAsync(TimeSpan.FromSeconds(5)); +``` + +## 安全保证 + +- **原子性**:锁获取、续期和释放使用原子 MongoDB 操作 +- **围栏**:围栏令牌防止丢失锁的进程执行操作 +- **崩溃恢复**:自动过期确保锁最终被释放,即使持有者崩溃 +- **无羊群效应**:随机和自适应退避防止同步的客户端使 MongoDB 过载 + +### 围栏令牌机制 + +围栏令牌确保即使进程失去其锁(由于网络分区、崩溃或超时),它也无法对受保护的资源执行操作: + +```mermaid +sequenceDiagram + participant PA as 进程 A + participant Resource as 受保护资源 + participant DB as MongoDB 锁 + + PA->>DB: 获取锁 → token=1 + DB->>PA: ✅ 已授予 + + PA->>Resource: 操作 token=1 + Resource->>Resource: 接受 (token >= last_seen) + + Note over DB: 网络分区 + + PB->>DB: 获取锁 → token=2 + DB->>PB: ✅ 已授予 + + PB->>Resource: 操作 token=2 + Resource->>Resource: 接受 (token >= last_seen) + + PA->>Resource: 操作 token=1 (已过期) + Resource->>Resource: 拒绝 ❌ (token < last_seen) +``` + +这个机制防止了"脑裂"场景,即两个进程都认为它们持有锁。 + +## 性能考虑 + +- 锁获取需要 1-2 个 MongoDB 操作(1 个用于获取,首次使用时可能 1 个用于索引创建) +- 锁续期在后台按 `ExtensionCadence` 间隔进行 +- 在竞争下,自适应退避相比固定随机间隔减少了对 MongoDB 的负载 +- `expiresAt` TTL 索引保持集合整洁,无需手动维护 + +## 自适应退避策略 + +当竞争高时,自适应退避策略会指数级增加获取尝试之间的等待时间: + +```mermaid +graph TD + A["尝试获取"] --> B{"锁可用?"} + B -->|是| C["✅ 已获取"] + B -->|否| D["连续失败: N 次"] + + D --> E["睡眠时间 = min * 1.5^N"] + E --> F["添加随机抖动 ±20%"] + F --> G["等待: max(min, sleepTime + jitter)"] + G --> H["重试"] + H --> A + + C --> I["重置失败计数"] + + style C fill:#90EE90 + style E fill:#FFD700 + style F fill:#FFA500 +``` + +### 退避对比图 + +#### 低竞争场景 + +| 尝试次数 | 策略 | 睡眠时间 | 备注 | +| -------- | ------ | -------- | ---------- | +| 第 1 次 | 随机 | 234ms | 不可预测 | +| 第 2 次 | 随机 | 567ms | 不可预测 | +| 第 3 次 | 随机 | 45ms | 不可预测 | +| 第 4 次 | 随机 | 689ms | 不可预测 | +| 第 1 次 | 自适应 | 10ms | 响应快 | +| 第 2 次 | 自适应 | 15ms | 指数增长 | +| 第 3 次 | 自适应 | 22ms | 控制退避 | +| 第 4 次 | 自适应 | 33ms | 仍然响应快 | + +#### 高竞争场景 + +| 尝试次数 | 策略 | 睡眠时间 | 影响 | +| -------- | ------ | -------- | ----------------- | +| 第 1 次 | 随机 | 234ms | 恒定高负载 | +| 第 2 次 | 随机 | 567ms | 恒定高负载 | +| 第 3 次 | 随机 | 45ms | 恒定高负载 | +| 第 4 次 | 随机 | 689ms | 恒定高负载 | +| 第 1 次 | 自适应 | 10ms | 开始响应快 | +| 第 2 次 | 自适应 | 15ms | 逐步退避 | +| 第 3 次 | 自适应 | 22ms | 减少 MongoDB 负载 | +| 第 4 次 | 自适应 | 150ms | 显著减少负载 | + +**关键优势**:自适应退避根据竞争水平自动调整,提供更好的资源利用率 + +## 框架支持 + +- **.NET Standard 2.1** 及更高版本 +- **.NET 8** 及更高版本 +- **.NET Framework 4.7.2** 及更高版本 + +## 备注 + +- 锁集合将在 `expiresAt` 字段上有一个索引,用于高效查询 +- 锁续期在后台自动进行 +- 如果锁续期失败,`HandleLostToken` 将被触发 +- 来自崩溃进程的陈旧锁将根据过期设置自动过期 From c01cc8311f65c29a2b5da084782717a1671dad18 Mon Sep 17 00:00:00 2001 From: Joes Date: Wed, 7 Jan 2026 11:22:01 +0800 Subject: [PATCH 17/21] fix: collection name and diagnostics --- src/Directory.Packages.props | 1 + src/DistributedLock.Azure/packages.lock.json | 114 ++++++------- .../DistributedLock.MongoDB.csproj | 4 + .../MongoDistributedLock.cs | 22 +-- .../packages.lock.json | 60 ++++--- src/DistributedLock.MySql/packages.lock.json | 119 ++++++------- src/DistributedLock.Oracle/packages.lock.json | 73 ++++---- .../packages.lock.json | 119 ++++++------- .../packages.lock.json | 114 ++++++------- src/DistributedLock.Tests/packages.lock.json | 18 +- src/DistributedLock/packages.lock.json | 161 +++++++++--------- src/DistributedLockTaker/packages.lock.json | 62 +++---- 12 files changed, 424 insertions(+), 443 deletions(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index c8bdc85e..f24895dc 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -22,6 +22,7 @@ + diff --git a/src/DistributedLock.Azure/packages.lock.json b/src/DistributedLock.Azure/packages.lock.json index f491b7e4..d8dfd66d 100644 --- a/src/DistributedLock.Azure/packages.lock.json +++ b/src/DistributedLock.Azure/packages.lock.json @@ -79,17 +79,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", - "dependencies": { - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.IO.Hashing": { "type": "Transitive", @@ -102,12 +93,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Memory.Data": { @@ -129,13 +120,13 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", @@ -217,6 +208,16 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, "System.ValueTuple": { "type": "CentralTransitive", "requested": "[4.5.0, )", @@ -300,17 +301,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", - "dependencies": { - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.IO.Hashing": { "type": "Transitive", @@ -323,12 +315,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Memory.Data": { @@ -342,13 +334,13 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Text.Encodings.Web": { "type": "Transitive", @@ -395,6 +387,16 @@ "dependencies": { "System.Threading.Tasks.Extensions": "4.5.4" } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } } }, ".NETStandard,Version=v2.1": { @@ -470,15 +472,6 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", - "dependencies": { - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.IO.Hashing": { "type": "Transitive", "resolved": "6.0.0", @@ -490,13 +483,8 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==" }, "System.Memory.Data": { "type": "Transitive", @@ -514,8 +502,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Text.Encodings.Web": { "type": "Transitive", @@ -546,6 +534,16 @@ }, "distributedlock.core": { "type": "Project" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } } } } diff --git a/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj b/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj index 7259e5a4..18a50aeb 100644 --- a/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj +++ b/src/DistributedLock.MongoDB/DistributedLock.MongoDB.csproj @@ -44,6 +44,10 @@ TRACE;DEBUG + + + + diff --git a/src/DistributedLock.MongoDB/MongoDistributedLock.cs b/src/DistributedLock.MongoDB/MongoDistributedLock.cs index d9faf44a..85fe0f82 100644 --- a/src/DistributedLock.MongoDB/MongoDistributedLock.cs +++ b/src/DistributedLock.MongoDB/MongoDistributedLock.cs @@ -2,10 +2,7 @@ using MongoDB.Bson; using MongoDB.Driver; using System.Collections.Concurrent; - -#if NET8_0_OR_GREATER using System.Diagnostics; -#endif namespace Medallion.Threading.MongoDB; @@ -18,14 +15,12 @@ public sealed partial class MongoDistributedLock : IInternalDistributedLock /// ActivitySource for distributed tracing and diagnostics /// private static readonly ActivitySource ActivitySource = new("DistributedLock.MongoDB", "1.0.0"); -#endif - // We want to ensure indexes at most once per process per (db, collection) + // We want to ensure indexes are created at most once per process per (database, collection) private static readonly ConcurrentDictionary> IndexInitializationTasks = new(StringComparer.Ordinal); private readonly string _collectionName; @@ -44,10 +39,10 @@ public sealed partial class MongoDistributedLock : IInternalDistributedLock /// Constructs a lock named using the provided and . - /// The locks will be stored in a collection named "DistributedLocks" by default. + /// The locks will be stored in a collection named "distributedLocks" by default. /// public MongoDistributedLock(string key, IMongoDatabase database, Action? options = null) - : this(key, database, "DistributedLocks", options) { } + : this(key, database, "distributedLocks", options) { } /// /// Constructs a lock named using the provided , , and @@ -142,11 +137,9 @@ public MongoDistributedLock(string key, IMongoDatabase database, string collecti private async ValueTask TryAcquireAsync(CancellationToken cancellationToken) { -#if NET8_0_OR_GREATER using var activity = ActivitySource.StartActivity("MongoDistributedLock.TryAcquire"); activity?.SetTag("lock.key", this.Key); activity?.SetTag("lock.collection", this._collectionName); -#endif var collection = this._database.GetCollection(this._collectionName); @@ -173,10 +166,8 @@ public MongoDistributedLock(string key, IMongoDatabase database, string collecti // Verify we actually got the lock if (result != null && result.LockId == lockId) { -#if NET8_0_OR_GREATER activity?.SetTag("lock.acquired", true); activity?.SetTag("lock.fencing_token", result.FencingToken); -#endif return new(collection, this.Key, lockId, @@ -184,10 +175,7 @@ public MongoDistributedLock(string key, IMongoDatabase database, string collecti this._options.Expiry, this._options.ExtensionCadence); } - -#if NET8_0_OR_GREATER activity?.SetTag("lock.acquired", false); -#endif return null; } @@ -234,7 +222,7 @@ private static UpdateDefinition CreateAcquireUpdate(string lo new BsonDocument { // Only overwrite lock fields when the previous lock is expired/missing - { "lockId", new BsonDocument("$cond", new BsonArray { expiredOrMissing, lockId, "$lockId" }) }, + { nameof(lockId), new BsonDocument("$cond", new BsonArray { expiredOrMissing, lockId, "$lockId" }) }, { "expiresAt", new BsonDocument("$cond", new BsonArray { expiredOrMissing, newExpiresAt, "$expiresAt" }) }, { "acquiredAt", new BsonDocument("$cond", new BsonArray { expiredOrMissing, "$$NOW", "$acquiredAt" }) }, { "fencingToken", new BsonDocument("$cond", new BsonArray { expiredOrMissing, newFencingToken, "$fencingToken" }) } @@ -251,7 +239,7 @@ private static Task EnsureIndexesCreatedAsync(IMongoCollection new Lazy(() => CreateIndexesAsync(collection))); + var lazy = IndexInitializationTasks.GetOrAdd(key, _ => new(() => CreateIndexesAsync(collection))); return lazy.Value; } diff --git a/src/DistributedLock.MongoDB/packages.lock.json b/src/DistributedLock.MongoDB/packages.lock.json index bfc80002..5d59545b 100644 --- a/src/DistributedLock.MongoDB/packages.lock.json +++ b/src/DistributedLock.MongoDB/packages.lock.json @@ -35,6 +35,16 @@ "ZstdSharp.Port": "0.7.3" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, "DnsClient": { "type": "Transitive", "resolved": "1.6.1", @@ -106,8 +116,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.IO": { "type": "Transitive", @@ -116,12 +126,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Net.Http": { @@ -134,8 +144,8 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime": { "type": "Transitive", @@ -144,8 +154,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", @@ -265,6 +275,16 @@ "ZstdSharp.Port": "0.7.3" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, "DnsClient": { "type": "Transitive", "resolved": "1.6.1", @@ -331,23 +351,13 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Security.AccessControl": { "type": "Transitive", diff --git a/src/DistributedLock.MySql/packages.lock.json b/src/DistributedLock.MySql/packages.lock.json index 2d1d4b8a..a0628767 100644 --- a/src/DistributedLock.MySql/packages.lock.json +++ b/src/DistributedLock.MySql/packages.lock.json @@ -64,37 +64,28 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "hYr3I9N9811e0Bjf2WNwAGGyTuAFbbTgX1RPLt/3Wbm68x3IGcX5Cl75CMmgT6WlNwLQ2tCCWfqYPpypjaf2xA==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Threading.Tasks.Extensions": { "type": "Transitive", @@ -120,6 +111,16 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, "System.ValueTuple": { "type": "CentralTransitive", "requested": "[4.5.0, )", @@ -190,37 +191,28 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "hYr3I9N9811e0Bjf2WNwAGGyTuAFbbTgX1RPLt/3Wbm68x3IGcX5Cl75CMmgT6WlNwLQ2tCCWfqYPpypjaf2xA==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Threading.Tasks.Extensions": { "type": "Transitive", @@ -244,6 +236,16 @@ "dependencies": { "System.Threading.Tasks.Extensions": "4.5.4" } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } } }, ".NETStandard,Version=v2.1": { @@ -297,37 +299,28 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "7.0.2", - "contentHash": "hYr3I9N9811e0Bjf2WNwAGGyTuAFbbTgX1RPLt/3Wbm68x3IGcX5Cl75CMmgT6WlNwLQ2tCCWfqYPpypjaf2xA==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "distributedlock.core": { "type": "Project" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } } } } diff --git a/src/DistributedLock.Oracle/packages.lock.json b/src/DistributedLock.Oracle/packages.lock.json index b511da3b..3a71b2b7 100644 --- a/src/DistributedLock.Oracle/packages.lock.json +++ b/src/DistributedLock.Oracle/packages.lock.json @@ -50,17 +50,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" - }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", - "dependencies": { - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.Formats.Asn1": { "type": "Transitive", @@ -74,23 +65,23 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Text.Encodings.Web": { "type": "Transitive", @@ -135,6 +126,16 @@ "Microsoft.Bcl.AsyncInterfaces": "[8.0.0, )", "System.ValueTuple": "[4.5.0, )" } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } } }, ".NETStandard,Version=v2.1": { @@ -188,15 +189,6 @@ "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", - "dependencies": { - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Diagnostics.PerformanceCounter": { "type": "Transitive", "resolved": "6.0.1", @@ -221,13 +213,8 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==" }, "System.Numerics.Vectors": { "type": "Transitive", @@ -236,8 +223,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Security.Cryptography.Cng": { "type": "Transitive", @@ -292,6 +279,16 @@ }, "distributedlock.core": { "type": "Project" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } } } } diff --git a/src/DistributedLock.Postgres/packages.lock.json b/src/DistributedLock.Postgres/packages.lock.json index 80b0048d..b346dd12 100644 --- a/src/DistributedLock.Postgres/packages.lock.json +++ b/src/DistributedLock.Postgres/packages.lock.json @@ -83,8 +83,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.Collections.Immutable": { "type": "Transitive", @@ -95,34 +95,25 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Text.Encodings.Web": { "type": "Transitive", @@ -180,6 +171,16 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, "System.ValueTuple": { "type": "CentralTransitive", "requested": "[4.5.0, )", @@ -269,8 +270,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.Collections.Immutable": { "type": "Transitive", @@ -281,34 +282,25 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Text.Encodings.Web": { "type": "Transitive", @@ -363,6 +355,16 @@ "dependencies": { "System.Threading.Tasks.Extensions": "4.5.4" } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } } }, ".NETStandard,Version=v2.1": { @@ -440,34 +442,15 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } - }, - "System.Numerics.Vectors": { - "type": "Transitive", - "resolved": "4.4.0", - "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Text.Encodings.Web": { "type": "Transitive", @@ -507,6 +490,16 @@ }, "distributedlock.core": { "type": "Project" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } } }, "net8.0": { diff --git a/src/DistributedLock.SqlServer/packages.lock.json b/src/DistributedLock.SqlServer/packages.lock.json index 3ba9674b..89f7def3 100644 --- a/src/DistributedLock.SqlServer/packages.lock.json +++ b/src/DistributedLock.SqlServer/packages.lock.json @@ -172,8 +172,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.ClientModel": { "type": "Transitive", @@ -192,15 +192,6 @@ "System.Security.Permissions": "6.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", - "dependencies": { - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.IdentityModel.Tokens.Jwt": { "type": "Transitive", "resolved": "6.35.0", @@ -221,12 +212,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Memory.Data": { @@ -248,13 +239,13 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", @@ -368,6 +359,16 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, "System.ValueTuple": { "type": "CentralTransitive", "requested": "[4.5.0, )", @@ -579,8 +580,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.ClientModel": { "type": "Transitive", @@ -600,15 +601,6 @@ "System.Security.Permissions": "6.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", - "dependencies": { - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.IdentityModel.Tokens.Jwt": { "type": "Transitive", "resolved": "6.35.0", @@ -643,12 +635,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Memory.Data": { @@ -662,8 +654,8 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Reflection": { "type": "Transitive", @@ -706,8 +698,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Runtime.Loader": { "type": "Transitive", @@ -828,6 +820,16 @@ "dependencies": { "System.Threading.Tasks.Extensions": "4.5.4" } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } } }, ".NETStandard,Version=v2.1": { @@ -1053,15 +1055,6 @@ "System.Security.Permissions": "6.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", - "dependencies": { - "System.Memory": "4.5.4", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.IdentityModel.Tokens.Jwt": { "type": "Transitive", "resolved": "6.35.0", @@ -1096,13 +1089,8 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==" }, "System.Memory.Data": { "type": "Transitive", @@ -1159,8 +1147,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Runtime.Loader": { "type": "Transitive", @@ -1269,6 +1257,16 @@ }, "distributedlock.core": { "type": "Project" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } } } } diff --git a/src/DistributedLock.Tests/packages.lock.json b/src/DistributedLock.Tests/packages.lock.json index 9178bc4c..88ac826e 100644 --- a/src/DistributedLock.Tests/packages.lock.json +++ b/src/DistributedLock.Tests/packages.lock.json @@ -356,14 +356,6 @@ "System.Security.Cryptography.ProtectedData": "8.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "8.0.0", @@ -449,8 +441,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" }, "System.Security.AccessControl": { "type": "Transitive", @@ -670,6 +662,12 @@ "Pipelines.Sockets.Unofficial": "2.2.8" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==" + }, "System.Threading.AccessControl": { "type": "CentralTransitive", "requested": "[8.0.0, )", diff --git a/src/DistributedLock/packages.lock.json b/src/DistributedLock/packages.lock.json index ad3986a5..2ab4063b 100644 --- a/src/DistributedLock/packages.lock.json +++ b/src/DistributedLock/packages.lock.json @@ -196,8 +196,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.ClientModel": { "type": "Transitive", @@ -225,15 +225,6 @@ "System.Security.Permissions": "6.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.IdentityModel.Tokens.Jwt": { "type": "Transitive", "resolved": "6.35.0", @@ -278,12 +269,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Memory.Data": { @@ -305,13 +296,13 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", @@ -553,6 +544,16 @@ "System.Threading.Channels": "5.0.0" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, "System.ValueTuple": { "type": "CentralTransitive", "requested": "[4.5.0, )", @@ -799,8 +800,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.ClientModel": { "type": "Transitive", @@ -828,15 +829,6 @@ "System.Security.Permissions": "6.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Formats.Asn1": { "type": "Transitive", "resolved": "8.0.1", @@ -896,12 +888,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Memory.Data": { @@ -923,8 +915,8 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime": { "type": "Transitive", @@ -933,8 +925,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", @@ -1088,7 +1080,8 @@ "type": "Project", "dependencies": { "DistributedLock.Core": "[1.0.8, )", - "MongoDB.Driver": "[3.5.2, )" + "MongoDB.Driver": "[3.5.2, )", + "System.Diagnostics.DiagnosticSource": "[10.0.1, )" } }, "distributedlock.mysql": { @@ -1234,6 +1227,16 @@ "System.Threading.Channels": "5.0.0" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, "ZooKeeperNetEx": { "type": "CentralTransitive", "requested": "[3.4.12.4, )", @@ -1462,8 +1465,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.ClientModel": { "type": "Transitive", @@ -1492,15 +1495,6 @@ "System.Security.Permissions": "6.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.IdentityModel.Tokens.Jwt": { "type": "Transitive", "resolved": "6.35.0", @@ -1554,12 +1548,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Memory.Data": { @@ -1573,8 +1567,8 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Reflection": { "type": "Transitive", @@ -1617,8 +1611,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Runtime.Loader": { "type": "Transitive", @@ -1874,6 +1868,16 @@ "System.Threading.Channels": "5.0.0" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, "System.Threading.AccessControl": { "type": "CentralTransitive", "requested": "[8.0.0, )", @@ -2163,15 +2167,6 @@ "System.Security.Permissions": "6.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Diagnostics.PerformanceCounter": { "type": "Transitive", "resolved": "6.0.1", @@ -2247,13 +2242,8 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" - } + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==" }, "System.Memory.Data": { "type": "Transitive", @@ -2310,8 +2300,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Runtime.Loader": { "type": "Transitive", @@ -2459,7 +2449,8 @@ "type": "Project", "dependencies": { "DistributedLock.Core": "[1.0.8, )", - "MongoDB.Driver": "[3.5.2, )" + "MongoDB.Driver": "[3.5.2, )", + "System.Diagnostics.DiagnosticSource": "[10.0.1, )" } }, "distributedlock.mysql": { @@ -2609,6 +2600,16 @@ "System.Threading.Channels": "5.0.0" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, "System.Threading.AccessControl": { "type": "CentralTransitive", "requested": "[8.0.0, )", diff --git a/src/DistributedLockTaker/packages.lock.json b/src/DistributedLockTaker/packages.lock.json index 83e81fa8..ff6bb646 100644 --- a/src/DistributedLockTaker/packages.lock.json +++ b/src/DistributedLockTaker/packages.lock.json @@ -208,8 +208,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.5.1", - "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + "resolved": "4.6.1", + "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" }, "System.ClientModel": { "type": "Transitive", @@ -237,15 +237,6 @@ "System.Security.Permissions": "6.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "8.0.0", - "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Formats.Asn1": { "type": "Transitive", "resolved": "8.0.1", @@ -305,12 +296,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.5", - "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "resolved": "4.6.3", + "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.3" + "System.Buffers": "4.6.1", + "System.Numerics.Vectors": "4.6.1", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" } }, "System.Memory.Data": { @@ -332,8 +323,8 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.5.0", - "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + "resolved": "4.6.1", + "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" }, "System.Runtime": { "type": "Transitive", @@ -342,8 +333,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "6.1.2", + "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", @@ -512,7 +503,8 @@ "type": "Project", "dependencies": { "DistributedLock.Core": "[1.0.8, )", - "MongoDB.Driver": "[3.5.2, )" + "MongoDB.Driver": "[3.5.2, )", + "System.Diagnostics.DiagnosticSource": "[10.0.1, )" } }, "distributedlock.mysql": { @@ -658,6 +650,16 @@ "System.Threading.Channels": "5.0.0" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", + "dependencies": { + "System.Memory": "4.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.1.2" + } + }, "ZooKeeperNetEx": { "type": "CentralTransitive", "requested": "[3.4.12.4, )", @@ -1023,14 +1025,6 @@ "System.Security.Cryptography.ProtectedData": "8.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "Transitive", - "resolved": "6.0.1", - "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - } - }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "8.0.0", @@ -1111,8 +1105,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + "resolved": "5.0.0", + "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" }, "System.Security.AccessControl": { "type": "Transitive", @@ -1332,6 +1326,12 @@ "Pipelines.Sockets.Unofficial": "2.2.8" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "CentralTransitive", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==" + }, "System.Threading.AccessControl": { "type": "CentralTransitive", "requested": "[8.0.0, )", From 0cb242f9606f885a5869b6a50d27605bd8238bd2 Mon Sep 17 00:00:00 2001 From: Joes Date: Wed, 7 Jan 2026 11:29:57 +0800 Subject: [PATCH 18/21] fix: change CPM condition introduction --- src/Directory.Packages.props | 2 +- src/DistributedLock.Azure/packages.lock.json | 82 +++++++++---------- src/DistributedLock.MySql/packages.lock.json | 82 +++++++++---------- .../packages.lock.json | 82 +++++++++---------- .../packages.lock.json | 82 +++++++++---------- src/DistributedLock.Tests/packages.lock.json | 18 ++-- src/DistributedLock/packages.lock.json | 82 +++++++++---------- src/DistributedLockTaker/packages.lock.json | 18 ++-- 8 files changed, 221 insertions(+), 227 deletions(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index f24895dc..85a8c98c 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -22,7 +22,7 @@ - + diff --git a/src/DistributedLock.Azure/packages.lock.json b/src/DistributedLock.Azure/packages.lock.json index d8dfd66d..d1945781 100644 --- a/src/DistributedLock.Azure/packages.lock.json +++ b/src/DistributedLock.Azure/packages.lock.json @@ -79,8 +79,17 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } }, "System.IO.Hashing": { "type": "Transitive", @@ -93,12 +102,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.6.3", - "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", "dependencies": { - "System.Buffers": "4.6.1", - "System.Numerics.Vectors": "4.6.1", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, "System.Memory.Data": { @@ -120,13 +129,13 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.1.2", - "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", @@ -208,16 +217,6 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", - "dependencies": { - "System.Memory": "4.6.3", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" - } - }, "System.ValueTuple": { "type": "CentralTransitive", "requested": "[4.5.0, )", @@ -301,8 +300,17 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } }, "System.IO.Hashing": { "type": "Transitive", @@ -315,12 +323,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.6.3", - "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", "dependencies": { - "System.Buffers": "4.6.1", - "System.Numerics.Vectors": "4.6.1", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, "System.Memory.Data": { @@ -334,13 +342,13 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.1.2", - "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, "System.Text.Encodings.Web": { "type": "Transitive", @@ -387,16 +395,6 @@ "dependencies": { "System.Threading.Tasks.Extensions": "4.5.4" } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", - "dependencies": { - "System.Memory": "4.6.3", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" - } } }, ".NETStandard,Version=v2.1": { diff --git a/src/DistributedLock.MySql/packages.lock.json b/src/DistributedLock.MySql/packages.lock.json index a0628767..171bb2d4 100644 --- a/src/DistributedLock.MySql/packages.lock.json +++ b/src/DistributedLock.MySql/packages.lock.json @@ -64,28 +64,37 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "hYr3I9N9811e0Bjf2WNwAGGyTuAFbbTgX1RPLt/3Wbm68x3IGcX5Cl75CMmgT6WlNwLQ2tCCWfqYPpypjaf2xA==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } }, "System.Memory": { "type": "Transitive", - "resolved": "4.6.3", - "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", "dependencies": { - "System.Buffers": "4.6.1", - "System.Numerics.Vectors": "4.6.1", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.1.2", - "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, "System.Threading.Tasks.Extensions": { "type": "Transitive", @@ -111,16 +120,6 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", - "dependencies": { - "System.Memory": "4.6.3", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" - } - }, "System.ValueTuple": { "type": "CentralTransitive", "requested": "[4.5.0, )", @@ -191,28 +190,37 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "7.0.2", + "contentHash": "hYr3I9N9811e0Bjf2WNwAGGyTuAFbbTgX1RPLt/3Wbm68x3IGcX5Cl75CMmgT6WlNwLQ2tCCWfqYPpypjaf2xA==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } }, "System.Memory": { "type": "Transitive", - "resolved": "4.6.3", - "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", "dependencies": { - "System.Buffers": "4.6.1", - "System.Numerics.Vectors": "4.6.1", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.1.2", - "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, "System.Threading.Tasks.Extensions": { "type": "Transitive", @@ -236,16 +244,6 @@ "dependencies": { "System.Threading.Tasks.Extensions": "4.5.4" } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", - "dependencies": { - "System.Memory": "4.6.3", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" - } } }, ".NETStandard,Version=v2.1": { diff --git a/src/DistributedLock.Postgres/packages.lock.json b/src/DistributedLock.Postgres/packages.lock.json index b346dd12..a469c1e9 100644 --- a/src/DistributedLock.Postgres/packages.lock.json +++ b/src/DistributedLock.Postgres/packages.lock.json @@ -83,8 +83,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, "System.Collections.Immutable": { "type": "Transitive", @@ -95,25 +95,34 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "System.Memory": { "type": "Transitive", - "resolved": "4.6.3", - "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", "dependencies": { - "System.Buffers": "4.6.1", - "System.Numerics.Vectors": "4.6.1", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.1.2", - "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, "System.Text.Encodings.Web": { "type": "Transitive", @@ -171,16 +180,6 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", - "dependencies": { - "System.Memory": "4.6.3", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" - } - }, "System.ValueTuple": { "type": "CentralTransitive", "requested": "[4.5.0, )", @@ -270,8 +269,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, "System.Collections.Immutable": { "type": "Transitive", @@ -282,25 +281,34 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "System.Memory": { "type": "Transitive", - "resolved": "4.6.3", - "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", "dependencies": { - "System.Buffers": "4.6.1", - "System.Numerics.Vectors": "4.6.1", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" + "resolved": "4.4.0", + "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.1.2", - "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, "System.Text.Encodings.Web": { "type": "Transitive", @@ -355,16 +363,6 @@ "dependencies": { "System.Threading.Tasks.Extensions": "4.5.4" } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", - "dependencies": { - "System.Memory": "4.6.3", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" - } } }, ".NETStandard,Version=v2.1": { diff --git a/src/DistributedLock.SqlServer/packages.lock.json b/src/DistributedLock.SqlServer/packages.lock.json index 89f7def3..4d56c89d 100644 --- a/src/DistributedLock.SqlServer/packages.lock.json +++ b/src/DistributedLock.SqlServer/packages.lock.json @@ -172,8 +172,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, "System.ClientModel": { "type": "Transitive", @@ -192,6 +192,15 @@ "System.Security.Permissions": "6.0.0" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "System.IdentityModel.Tokens.Jwt": { "type": "Transitive", "resolved": "6.35.0", @@ -212,12 +221,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.6.3", - "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", "dependencies": { - "System.Buffers": "4.6.1", - "System.Numerics.Vectors": "4.6.1", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, "System.Memory.Data": { @@ -239,13 +248,13 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.1.2", - "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", @@ -359,16 +368,6 @@ "System.Threading.Tasks.Extensions": "4.5.4" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", - "dependencies": { - "System.Memory": "4.6.3", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" - } - }, "System.ValueTuple": { "type": "CentralTransitive", "requested": "[4.5.0, )", @@ -580,8 +579,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, "System.ClientModel": { "type": "Transitive", @@ -601,6 +600,15 @@ "System.Security.Permissions": "6.0.0" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "System.IdentityModel.Tokens.Jwt": { "type": "Transitive", "resolved": "6.35.0", @@ -635,12 +643,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.6.3", - "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", "dependencies": { - "System.Buffers": "4.6.1", - "System.Numerics.Vectors": "4.6.1", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, "System.Memory.Data": { @@ -654,8 +662,8 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, "System.Reflection": { "type": "Transitive", @@ -698,8 +706,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.1.2", - "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, "System.Runtime.Loader": { "type": "Transitive", @@ -820,16 +828,6 @@ "dependencies": { "System.Threading.Tasks.Extensions": "4.5.4" } - }, - "System.Diagnostics.DiagnosticSource": { - "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", - "dependencies": { - "System.Memory": "4.6.3", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" - } } }, ".NETStandard,Version=v2.1": { diff --git a/src/DistributedLock.Tests/packages.lock.json b/src/DistributedLock.Tests/packages.lock.json index 88ac826e..9178bc4c 100644 --- a/src/DistributedLock.Tests/packages.lock.json +++ b/src/DistributedLock.Tests/packages.lock.json @@ -356,6 +356,14 @@ "System.Security.Cryptography.ProtectedData": "8.0.0" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "8.0.0", @@ -441,8 +449,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, "System.Security.AccessControl": { "type": "Transitive", @@ -662,12 +670,6 @@ "Pipelines.Sockets.Unofficial": "2.2.8" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==" - }, "System.Threading.AccessControl": { "type": "CentralTransitive", "requested": "[8.0.0, )", diff --git a/src/DistributedLock/packages.lock.json b/src/DistributedLock/packages.lock.json index 2ab4063b..651b9229 100644 --- a/src/DistributedLock/packages.lock.json +++ b/src/DistributedLock/packages.lock.json @@ -196,8 +196,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, "System.ClientModel": { "type": "Transitive", @@ -225,6 +225,15 @@ "System.Security.Permissions": "6.0.0" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "System.IdentityModel.Tokens.Jwt": { "type": "Transitive", "resolved": "6.35.0", @@ -269,12 +278,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.6.3", - "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", "dependencies": { - "System.Buffers": "4.6.1", - "System.Numerics.Vectors": "4.6.1", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, "System.Memory.Data": { @@ -296,13 +305,13 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.1.2", - "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, "System.Runtime.InteropServices.RuntimeInformation": { "type": "Transitive", @@ -544,16 +553,6 @@ "System.Threading.Channels": "5.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", - "dependencies": { - "System.Memory": "4.6.3", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" - } - }, "System.ValueTuple": { "type": "CentralTransitive", "requested": "[4.5.0, )", @@ -1465,8 +1464,8 @@ }, "System.Buffers": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "N8GXpmiLMtljq7gwvyS+1QvKT/W2J8sNAvx+HVg4NGmsG/H+2k/y9QI23auLJRterrzCiDH+IWAw4V/GPwsMlw==" + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, "System.ClientModel": { "type": "Transitive", @@ -1495,6 +1494,15 @@ "System.Security.Permissions": "6.0.0" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "System.IdentityModel.Tokens.Jwt": { "type": "Transitive", "resolved": "6.35.0", @@ -1548,12 +1556,12 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.6.3", - "contentHash": "qdcDOgnFZY40+Q9876JUHnlHu7bosOHX8XISRoH94fwk6hgaeQGSgfZd8srWRZNt5bV9ZW2TljcegDNxsf+96A==", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", "dependencies": { - "System.Buffers": "4.6.1", - "System.Numerics.Vectors": "4.6.1", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.4.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" } }, "System.Memory.Data": { @@ -1567,8 +1575,8 @@ }, "System.Numerics.Vectors": { "type": "Transitive", - "resolved": "4.6.1", - "contentHash": "sQxefTnhagrhoq2ReR0D/6K0zJcr9Hrd6kikeXsA1I8kOCboTavcUC4r7TSfpKFeE163uMuxZcyfO1mGO3EN8Q==" + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, "System.Reflection": { "type": "Transitive", @@ -1611,8 +1619,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "6.1.2", - "contentHash": "2hBr6zdbIBTDE3EhK7NSVNdX58uTK6iHW/P/Axmm9sl1xoGSLqDvMtpecn226TNwHByFokYwJmt/aQQNlO5CRw==" + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, "System.Runtime.Loader": { "type": "Transitive", @@ -1868,16 +1876,6 @@ "System.Threading.Channels": "5.0.0" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==", - "dependencies": { - "System.Memory": "4.6.3", - "System.Runtime.CompilerServices.Unsafe": "6.1.2" - } - }, "System.Threading.AccessControl": { "type": "CentralTransitive", "requested": "[8.0.0, )", diff --git a/src/DistributedLockTaker/packages.lock.json b/src/DistributedLockTaker/packages.lock.json index ff6bb646..9d3c2957 100644 --- a/src/DistributedLockTaker/packages.lock.json +++ b/src/DistributedLockTaker/packages.lock.json @@ -1025,6 +1025,14 @@ "System.Security.Cryptography.ProtectedData": "8.0.0" } }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.1", + "contentHash": "KiLYDu2k2J82Q9BJpWiuQqCkFjRBWVq4jDzKKWawVi9KWzyD0XG3cmfX0vqTQlL14Wi9EufJrbL0+KCLTbqWiQ==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "System.Diagnostics.EventLog": { "type": "Transitive", "resolved": "8.0.0", @@ -1105,8 +1113,8 @@ }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", - "resolved": "5.0.0", - "contentHash": "ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==" + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" }, "System.Security.AccessControl": { "type": "Transitive", @@ -1326,12 +1334,6 @@ "Pipelines.Sockets.Unofficial": "2.2.8" } }, - "System.Diagnostics.DiagnosticSource": { - "type": "CentralTransitive", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "wVYO4/71Pk177uQ3TG8ZQFS3Pnmr98cF9pYxnpuIb/bMnbEWsdZZoLU/euv29mfSi2/Iuypj0TRUchPk7aqBGg==" - }, "System.Threading.AccessControl": { "type": "CentralTransitive", "requested": "[8.0.0, )", From 8adb4b9a08b849d1775fa42d4fd70b26d03869e5 Mon Sep 17 00:00:00 2001 From: Joes Date: Wed, 7 Jan 2026 15:54:30 +0800 Subject: [PATCH 19/21] fix: pass all tests, change collection name to `distributed.locks` --- .../AssemblyAttributes.cs | 3 +- .../MongoDistributedLock.cs | 51 ++++-- ...MongoDistributedSynchronizationProvider.cs | 6 +- src/DistributedLock.MongoDB/README.md | 4 +- src/DistributedLock.MongoDB/README.zh-CN.md | 4 +- .../MongoDB/TestingMongoDbProviders.cs | 13 +- .../TestingMongoDbSynchronizationStrategy.cs | 27 +++- .../Shared/MongoDbCredentials.cs | 15 +- .../Tests/MongoDB/MongoDistributedLockTest.cs | 153 +++++++++++++++++- src/DistributedLockTaker/Program.cs | 7 +- 10 files changed, 246 insertions(+), 37 deletions(-) diff --git a/src/DistributedLock.MongoDB/AssemblyAttributes.cs b/src/DistributedLock.MongoDB/AssemblyAttributes.cs index f3102190..12a8b7af 100644 --- a/src/DistributedLock.MongoDB/AssemblyAttributes.cs +++ b/src/DistributedLock.MongoDB/AssemblyAttributes.cs @@ -2,4 +2,5 @@ [assembly: InternalsVisibleTo( - "DistributedLock.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fd3af56ccc8ed94fffe25bfd651e6a5674f8f20a76d37de800dd0f7380e04f0fde2da6fa200380b14fe398605b6f470c87e5e0a0bf39ae871f07536a4994aa7a0057c4d3bcedc8fef3eecb0c88c2024a1b3289305c2393acd9fb9f9a42d0bd7826738ce864d507575ea3a1fe1746ab19823303269f79379d767949807f494be8")] \ No newline at end of file + "DistributedLock.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fd3af56ccc8ed94fffe25bfd651e6a5674f8f20a76d37de800dd0f7380e04f0fde2da6fa200380b14fe398605b6f470c87e5e0a0bf39ae871f07536a4994aa7a0057c4d3bcedc8fef3eecb0c88c2024a1b3289305c2393acd9fb9f9a42d0bd7826738ce864d507575ea3a1fe1746ab19823303269f79379d767949807f494be8")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoDistributedLock.cs b/src/DistributedLock.MongoDB/MongoDistributedLock.cs index 85fe0f82..6e141e4b 100644 --- a/src/DistributedLock.MongoDB/MongoDistributedLock.cs +++ b/src/DistributedLock.MongoDB/MongoDistributedLock.cs @@ -21,7 +21,7 @@ public sealed partial class MongoDistributedLock : IInternalDistributedLock> IndexInitializationTasks = new(StringComparer.Ordinal); + private static readonly ConcurrentDictionary>> IndexInitializationTasks = new(StringComparer.Ordinal); private readonly string _collectionName; private readonly IMongoDatabase _database; @@ -42,7 +42,7 @@ public sealed partial class MongoDistributedLock : IInternalDistributedLock public MongoDistributedLock(string key, IMongoDatabase database, Action? options = null) - : this(key, database, "distributedLocks", options) { } + : this(key, database, "distributed.locks", options) { } /// /// Constructs a lock named using the provided , , and @@ -50,10 +50,9 @@ public MongoDistributedLock(string key, IMongoDatabase database, Action public MongoDistributedLock(string key, IMongoDatabase database, string collectionName, Action? options = null) { - if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException(nameof(key)); } this._database = database ?? throw new ArgumentNullException(nameof(database)); this._collectionName = collectionName ?? throw new ArgumentNullException(nameof(collectionName)); - this.Key = key; + this.Key = key ?? throw new ArgumentNullException(nameof(key)); this._options = MongoDistributedSynchronizationOptionsBuilder.GetOptions(options); } @@ -108,7 +107,7 @@ public MongoDistributedLock(string key, IMongoDatabase database, string collecti try { - await Task.Delay(TimeSpan.FromMilliseconds(finalSleepMs), effectiveToken).ConfigureAwait(false); + await SyncViaAsync.Delay(TimeSpan.FromMilliseconds(finalSleepMs), effectiveToken).ConfigureAwait(false); } catch (OperationCanceledException) when (timeoutCts?.IsCancellationRequested == true && !cancellationToken.IsCancellationRequested) { @@ -161,7 +160,15 @@ public MongoDistributedLock(string key, IMongoDatabase database, string collecti ReturnDocument = ReturnDocument.After }; - var result = await collection.FindOneAndUpdateAsync(filter, update, options, cancellationToken).ConfigureAwait(false); + MongoLockDocument? result; + if (SyncViaAsync.IsSynchronous) + { + result = collection.FindOneAndUpdate(filter, update, options, cancellationToken); + } + else + { + result = await collection.FindOneAndUpdateAsync(filter, update, options, cancellationToken).ConfigureAwait(false); + } // Verify we actually got the lock if (result != null && result.LockId == lockId) @@ -237,13 +244,36 @@ private static Task EnsureIndexesCreatedAsync(IMongoCollection new(() => CreateIndexesAsync(collection))); - return lazy.Value; + + // If we are executing synchronously, we must ensure the task is complete. + // This covers two cases: + // 1. We just created the task. CreateIndexesAsync will run synchronously and return a completed task. + // 2. The task was created by a previous async caller and is still running. We must block until it finishes. + if (SyncViaAsync.IsSynchronous && !lazy.Value.IsCompleted) + { + lazy.Value.GetAwaiter().GetResult(); + } + + var task = lazy.Value; + if (task.IsCompleted && !task.Result) + { + // If the task failed (returned false), we remove it so we can try again next time. + // Note: worst case we remove a *new* valid task if a race happens, which is fine (just extra work). + ((ICollection>>>)IndexInitializationTasks).Remove(new KeyValuePair>>(key, lazy)); + } + + return task; } - private static async Task CreateIndexesAsync(IMongoCollection collection) + private static async Task CreateIndexesAsync(IMongoCollection collection) { try { @@ -256,17 +286,20 @@ private static async Task CreateIndexesAsync(IMongoCollection }; var indexModel = new CreateIndexModel(indexKeys, indexOptions); await collection.Indexes.CreateOneAsync(indexModel, cancellationToken: CancellationToken.None).ConfigureAwait(false); + return true; } catch (MongoCommandException ex) when ( ex.CodeName is "IndexOptionsConflict" or "IndexKeySpecsConflict" or "IndexAlreadyExists") { // Index already exists with same or different options - this is acceptable. // The existing index will still handle TTL cleanup. + return true; } catch (MongoException) { // Other MongoDB errors (network, auth, etc.) - swallow to avoid blocking lock acquisition. // The lock will still work correctly; TTL cleanup is a best-effort optimization. + return false; } } } \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/MongoDistributedSynchronizationProvider.cs b/src/DistributedLock.MongoDB/MongoDistributedSynchronizationProvider.cs index c3a46ace..a6e7f1ed 100644 --- a/src/DistributedLock.MongoDB/MongoDistributedSynchronizationProvider.cs +++ b/src/DistributedLock.MongoDB/MongoDistributedSynchronizationProvider.cs @@ -13,10 +13,10 @@ public sealed class MongoDistributedSynchronizationProvider : IDistributedLockPr /// /// Constructs a that connects to the provided - /// and uses the provided . Locks will be stored in a collection named "DistributedLocks" by default. + /// and uses the provided . Locks will be stored in a collection named "distributed.locks" by default. /// public MongoDistributedSynchronizationProvider(IMongoDatabase database, Action? options = null) - : this(database, "DistributedLocks", options) { } + : this(database, "distributed.locks", options) { } /// /// Constructs a that connects to the provided , @@ -39,6 +39,6 @@ public MongoDistributedLock CreateLock(string name) IDistributedLock IDistributedLockProvider.CreateLock(string name) { - return CreateLock(name); + return this.CreateLock(name); } } \ No newline at end of file diff --git a/src/DistributedLock.MongoDB/README.md b/src/DistributedLock.MongoDB/README.md index 6a9ecc26..c154c659 100644 --- a/src/DistributedLock.MongoDB/README.md +++ b/src/DistributedLock.MongoDB/README.md @@ -109,7 +109,7 @@ var @lock = new MongoDistributedLock( ### Custom Collection Name -By default, locks are stored in a collection named `"DistributedLocks"`. You can specify a custom collection name: +By default, locks are stored in a collection named `"distributed.locks"`. You can specify a custom collection name: ```csharp // Direct lock creation with custom collection @@ -190,7 +190,7 @@ graph TB Handle["MongoDistributedLockHandle"] MongoDB[("MongoDB Database")] - Collection["DistributedLocks Collection"] + Collection["distributed.locks Collection"] TTLIndex["TTL Index on expiresAt"] Client -->|Creates| Lock diff --git a/src/DistributedLock.MongoDB/README.zh-CN.md b/src/DistributedLock.MongoDB/README.zh-CN.md index bb892c84..7e418f26 100644 --- a/src/DistributedLock.MongoDB/README.zh-CN.md +++ b/src/DistributedLock.MongoDB/README.zh-CN.md @@ -109,7 +109,7 @@ var @lock = new MongoDistributedLock( ### 自定义集合名称 -默认情况下,锁存储在名为 `"DistributedLocks"` 的集合中。您可以指定自定义集合名称: +默认情况下,锁存储在名为 `"distributed.locks"` 的集合中。您可以指定自定义集合名称: ```csharp // 直接创建锁并使用自定义集合 @@ -190,7 +190,7 @@ graph TB Handle["MongoDistributedLockHandle"] MongoDB[("MongoDB 数据库")] - Collection["DistributedLocks 集合"] + Collection["distributed.locks 集合"] TTLIndex["expiresAt 上的 TTL 索引"] Client -->|创建| Lock diff --git a/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbProviders.cs b/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbProviders.cs index d873e300..251ea0ca 100644 --- a/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbProviders.cs +++ b/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbProviders.cs @@ -5,15 +5,16 @@ namespace Medallion.Threading.Tests.MongoDB; public sealed class TestingMongoDistributedLockProvider : TestingLockProvider { - private readonly string _collectionName = "DistributedLocks_" + Guid.NewGuid().ToString("N"); + private const string CollectionName = "distributed.locks"; private readonly IMongoDatabase _database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); public override IDistributedLock CreateLockWithExactName(string name) { - var @lock = new MongoDistributedLock(name, _database, _collectionName); - Strategy.KillHandleAction = () => + // Use a short expiry to make tests like TestHandleLostTriggersCorrectly run faster + var @lock = new MongoDistributedLock(name, _database, CollectionName, options => options.Expiry(TimeSpan.FromSeconds(5))); + this.Strategy.KillHandleAction = () => { - var collection = _database.GetCollection(_collectionName); + var collection = _database.GetCollection(CollectionName); collection.DeleteOne(Builders.Filter.Eq(d => d.Id, name)); }; return @lock; @@ -21,7 +22,7 @@ public override IDistributedLock CreateLockWithExactName(string name) public override string GetSafeName(string name) { - return new MongoDistributedLock(name, _database, _collectionName).Name; + return new MongoDistributedLock(name, _database, CollectionName).Name; } public override string GetCrossProcessLockType() @@ -34,7 +35,7 @@ public override void Dispose() // Clean up test collection try { - _database.DropCollection(_collectionName); + _database.DropCollection(CollectionName); } catch { diff --git a/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbSynchronizationStrategy.cs b/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbSynchronizationStrategy.cs index 4324c1d5..9dc6b114 100644 --- a/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbSynchronizationStrategy.cs +++ b/src/DistributedLock.Tests/Infrastructure/MongoDB/TestingMongoDbSynchronizationStrategy.cs @@ -1,3 +1,8 @@ +using Medallion.Threading.Tests; +using Medallion.Threading.Internal; +using Medallion.Threading.MongoDB; +using MongoDB.Driver; + namespace Medallion.Threading.Tests.MongoDB; public sealed class TestingMongoDbSynchronizationStrategy : TestingSynchronizationStrategy @@ -6,6 +11,26 @@ public sealed class TestingMongoDbSynchronizationStrategy : TestingSynchronizati public override void PrepareForHandleAbandonment() { - KillHandleAction?.Invoke(); + this.KillHandleAction?.Invoke(); + } + + public override void PerformAdditionalCleanupForHandleAbandonment() + { + this.KillHandleAction?.Invoke(); + } + + public override IDisposable? PrepareForHandleLost() + { + return new DisposableAction(() => this.KillHandleAction?.Invoke()); + } + + private class DisposableAction(Action action) : IDisposable + { + private Action? _action = action; + + public void Dispose() + { + Interlocked.Exchange(ref this._action, null)?.Invoke(); + } } } \ No newline at end of file diff --git a/src/DistributedLock.Tests/Infrastructure/Shared/MongoDbCredentials.cs b/src/DistributedLock.Tests/Infrastructure/Shared/MongoDbCredentials.cs index 8c8ce172..6769c40e 100644 --- a/src/DistributedLock.Tests/Infrastructure/Shared/MongoDbCredentials.cs +++ b/src/DistributedLock.Tests/Infrastructure/Shared/MongoDbCredentials.cs @@ -11,21 +11,16 @@ public static string GetConnectionString(string baseDirectory) { if (_connectionString != null) { return _connectionString; } var file = Path.GetFullPath(Path.Combine(baseDirectory, "..", "..", "..", "credentials", "mongodb.txt")); - if (File.Exists(file)) - { - _connectionString = File.ReadAllText(file).Trim(); - } - else - { - // Default local MongoDB connection - _connectionString = "mongodb://localhost:27017"; - } + _connectionString = File.Exists(file) + ? File.ReadAllText(file).Trim() + // Default local MongoDB connection + : "mongodb://localhost:27017"; return _connectionString; } public static IMongoDatabase GetDefaultDatabase(string baseDirectory) { var client = new MongoClient(GetConnectionString(baseDirectory)); - return client.GetDatabase("DistributedLockTests"); + return client.GetDatabase("distributedLockTests"); } } \ No newline at end of file diff --git a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs index 9c753478..7c8648d0 100644 --- a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs +++ b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs @@ -67,7 +67,7 @@ public async Task TestHandleLostToken() }); // Manually delete the lock document to simulate lock loss - var collection = database.GetCollection("DistributedLocks"); + var collection = database.GetCollection("distributed.locks"); await collection.DeleteOneAsync(Builders.Filter.Eq(d => d.Id, lockName)); // Wait a bit for the extension task to detect the loss @@ -113,8 +113,157 @@ public void TestValidatesConstructorParameters() { var database = new Mock(MockBehavior.Strict).Object; Assert.Throws(() => new MongoDistributedLock(null!, database)); - Assert.Throws(() => new MongoDistributedLock(string.Empty, database)); Assert.Throws(() => new MongoDistributedLock("key", null!)); Assert.Throws(() => new MongoDistributedLock("key", database, (string)null!)); } + + [Test] + public async Task TestIndexExistence() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + var collectionName = "TestIndex" + Guid.NewGuid().ToString("N"); + + var @lock = new MongoDistributedLock("lock", database, collectionName); + await using (await @lock.AcquireAsync()) + { + } + + var collection = database.GetCollection(collectionName); + using var cursor = await collection.Indexes.ListAsync(); + var indexes = await cursor.ToListAsync(); + + var ttlIndex = indexes.FirstOrDefault(i => i["name"] == "expiresAt_ttl"); + Assert.That(ttlIndex, Is.Not.Null, "TTL index should exist"); + Assert.That(ttlIndex!["expireAfterSeconds"].AsInt32, Is.EqualTo(0)); // check functionality + } + + [Test] + [Category("CI")] + public async Task TestIndexCreationIsScopedToCluster() + { + // Simulate two distinct databases with SAME name/collection but treated as different + var db1 = new Mock(MockBehavior.Strict); + var db2 = new Mock(MockBehavior.Strict); + + var coll1 = new Mock>(MockBehavior.Strict); + var coll2 = new Mock>(MockBehavior.Strict); + + // We can't easily mock ClusterId equality without deeper mocking, + // but verify that if we use the *same* logic we rely on unique behavior? + // Wait, unit testing static cache with mocks is tricky because state persists. + // We need a unique db/coll name to avoid interference from other tests. + var uniqueName = "db_" + Guid.NewGuid().ToString("N"); + SetupDb(db1, coll1, uniqueName, "locks"); + SetupDb(db2, coll2, uniqueName, "locks"); + + // We want to verify ConfigureIndexes is called on BOTH. + + // Setup index creation mocks + var idx1 = new Mock>(MockBehavior.Strict); + var idx2 = new Mock>(MockBehavior.Strict); + + coll1.Setup(c => c.Indexes).Returns(idx1.Object); + coll2.Setup(c => c.Indexes).Returns(idx2.Object); + + // Allow FindOneAndUpdate + coll1.Setup(c => c.FindOneAndUpdateAsync(It.IsAny>(), It.IsAny>(), It.IsAny>(), It.IsAny())) + .ReturnsAsync((MongoLockDocument)null!); + coll2.Setup(c => c.FindOneAndUpdateAsync(It.IsAny>(), It.IsAny>(), It.IsAny>(), It.IsAny())) + .ReturnsAsync((MongoLockDocument)null!); + + // Expect CreateOneAsync + idx1.Setup(i => i.CreateOneAsync(It.IsAny>(), It.IsAny(), It.IsAny())) + .ReturnsAsync("idx"); + idx2.Setup(i => i.CreateOneAsync(It.IsAny>(), It.IsAny(), It.IsAny())) + .ReturnsAsync("idx"); + + var lock1 = new MongoDistributedLock("k", db1.Object, "locks"); + var lock2 = new MongoDistributedLock("k", db2.Object, "locks"); + + await lock1.TryAcquireAsync(); + await lock2.TryAcquireAsync(); + + idx1.Verify(i => i.CreateOneAsync(It.IsAny>(), It.IsAny(), It.IsAny()), Times.Once, "First DB should create index"); + + idx2.Verify(i => i.CreateOneAsync(It.IsAny>(), It.IsAny(), It.IsAny()), Times.Once, "Second DB should create index too because it's a different instance"); + } + + private static void SetupDb(Mock db, Mock> coll, string dbName, string collName) + { + db.Setup(d => d.GetCollection(collName, null)).Returns(coll.Object); + var dbNs = new DatabaseNamespace(dbName); + var collNs = new CollectionNamespace(dbNs, collName); + + coll.Setup(c => c.CollectionNamespace).Returns(collNs); + coll.Setup(c => c.Database).Returns(db.Object); + db.Setup(d => d.DatabaseNamespace).Returns(dbNs); + + // Mock Client and Settings + var client = new Mock(MockBehavior.Strict); + // Ensure settings are distinct by adding a random server address + var settings = new MongoClientSettings { Servers = [new("host" + Guid.NewGuid().ToString("N"))] }; + client.Setup(c => c.Settings).Returns(settings); + db.Setup(d => d.Client).Returns(client.Object); + } + + [Test] + [Category("CI")] + public async Task TestIndexCreationFailureIsCached() + { + var db = new Mock(MockBehavior.Strict); + var coll = new Mock>(MockBehavior.Strict); + SetupDb(db, coll, "db_" + Guid.NewGuid().ToString("N"), "locks"); + + var idx = new Mock>(MockBehavior.Strict); + coll.Setup(c => c.Indexes).Returns(idx.Object); + + // Fail first time + idx.SetupSequence(i => i.CreateOneAsync(It.IsAny>(), It.IsAny(), It.IsAny())) + .ThrowsAsync(new MongoException("Test failure")) + .ReturnsAsync("idx"); + + // Allow FindOneAndUpdate + coll.Setup(c => c.FindOneAndUpdateAsync(It.IsAny>(), It.IsAny>(), It.IsAny>(), It.IsAny())) + .ReturnsAsync((MongoLockDocument)null!); + + var @lock = new MongoDistributedLock("k", db.Object, "locks"); + + // First acquire: fails to create index (swallowed), acquires lock + await @lock.TryAcquireAsync(); + + // Second acquire: should retry index creation if we fix it. + // Currently it caches the failed task, so it won't retry. + await @lock.TryAcquireAsync(); + + // Verify CreateOneAsync was called TWICE (proving retry). + // If the current bug exists, it will be called ONCE. + idx.Verify(i => i.CreateOneAsync(It.IsAny>(), It.IsAny(), It.IsAny()), Times.Exactly(2), "Should retry index creation after failure"); + } + + [Test] + public async Task TestLockDocumentStructure() + { + var database = MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory); + var lockName = TestHelper.UniqueName; + var collectionName = "locks_" + Guid.NewGuid().ToString("N"); + + var @lock = new MongoDistributedLock(lockName, database, collectionName, o => o.Expiry(TimeSpan.FromSeconds(10))); + await using var handle = await @lock.AcquireAsync(); + var collection = database.GetCollection(collectionName); + var doc = await collection.Find(Builders.Filter.Eq(d => d.Id, lockName)).FirstOrDefaultAsync(); + + Assert.That(doc, Is.Not.Null); + Assert.That(doc.LockId, Is.Not.Null); + Assert.That(doc.FencingToken, Is.GreaterThan(0)); + + // Allow for some clock skew/processing time + var now = DateTime.UtcNow; // Mongo assumes UTC usually, check implicit assumption + // BsonDateTime is usually UTC. + + // Depending on Mongo version and driver, dates are UTC. + // The lock sets expiresAt = $$NOW + 10s. + // Check that it is roughly in the future. + Assert.That(doc.ExpiresAt.ToUniversalTime(), Is.GreaterThan(DateTime.UtcNow.AddSeconds(5))); + Assert.That(doc.ExpiresAt.ToUniversalTime(), Is.LessThan(DateTime.UtcNow.AddSeconds(15))); + } } \ No newline at end of file diff --git a/src/DistributedLockTaker/Program.cs b/src/DistributedLockTaker/Program.cs index c15d0a5a..ee938f23 100644 --- a/src/DistributedLockTaker/Program.cs +++ b/src/DistributedLockTaker/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using Medallion.Threading.SqlServer; using Medallion.Threading.WaitHandles; using Medallion.Threading.Postgres; @@ -15,6 +15,8 @@ using Medallion.Threading.ZooKeeper; using Medallion.Threading.MySql; using Medallion.Threading.Oracle; +using Medallion.Threading.MongoDB; +using Medallion.Threading.Tests.MongoDB; namespace DistributedLockTaker; @@ -134,6 +136,9 @@ public static int Main(string[] args) ).AcquireAsync().Result; break; } + case nameof(MongoDistributedLock): + handle = new MongoDistributedLock(name, MongoDbCredentials.GetDefaultDatabase(Environment.CurrentDirectory), options => options.Expiry(TimeSpan.FromSeconds(5))).Acquire(); + break; default: Console.Error.WriteLine($"type: {type}"); return 123; From 91b5878ee605c9c61a67ddfe80e6c4a50996b92c Mon Sep 17 00:00:00 2001 From: Joes Date: Wed, 7 Jan 2026 15:58:45 +0800 Subject: [PATCH 20/21] =?UTF-8?q?fix:=20=E6=9B=B4=E6=94=B9Mongo=E5=88=86?= =?UTF-8?q?=E5=B8=83=E5=BC=8F=E9=94=81=E9=BB=98=E8=AE=A4=E9=9B=86=E5=90=88?= =?UTF-8?q?=E5=90=8D=E4=B8=BAdistributed.locks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将MongoDistributedLock构造函数中默认的锁集合名称从"distributedLocks"修改为"distributed.locks",以统一集合命名规范。此更改会影响新建锁时的默认存储集合。 --- src/DistributedLock.MongoDB/MongoDistributedLock.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DistributedLock.MongoDB/MongoDistributedLock.cs b/src/DistributedLock.MongoDB/MongoDistributedLock.cs index 6e141e4b..fc77d1ff 100644 --- a/src/DistributedLock.MongoDB/MongoDistributedLock.cs +++ b/src/DistributedLock.MongoDB/MongoDistributedLock.cs @@ -39,7 +39,7 @@ public sealed partial class MongoDistributedLock : IInternalDistributedLock /// Constructs a lock named using the provided and . - /// The locks will be stored in a collection named "distributedLocks" by default. + /// The locks will be stored in a collection named "distributed.locks" by default. /// public MongoDistributedLock(string key, IMongoDatabase database, Action? options = null) : this(key, database, "distributed.locks", options) { } From 80c0738cedd0fb340b87e0763af4718150671bfc Mon Sep 17 00:00:00 2001 From: Joe Du <13188169+joesdu@users.noreply.github.com> Date: Thu, 8 Jan 2026 00:21:54 +0800 Subject: [PATCH 21/21] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Tests/MongoDB/MongoDistributedLockTest.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs index 7c8648d0..5d4d2687 100644 --- a/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs +++ b/src/DistributedLock.Tests/Tests/MongoDB/MongoDistributedLockTest.cs @@ -256,9 +256,8 @@ public async Task TestLockDocumentStructure() Assert.That(doc.LockId, Is.Not.Null); Assert.That(doc.FencingToken, Is.GreaterThan(0)); - // Allow for some clock skew/processing time - var now = DateTime.UtcNow; // Mongo assumes UTC usually, check implicit assumption - // BsonDateTime is usually UTC. + // Allow for some clock skew/processing time. + // Mongo and BsonDateTime usually assume UTC; check implicit assumption. // Depending on Mongo version and driver, dates are UTC. // The lock sets expiresAt = $$NOW + 10s.