Skip to content

Commit a21e317

Browse files
authored
Merge pull request #232 from madelson/release-2.5.1
Release 2.5.1
2 parents ce4a071 + 737df17 commit a21e317

File tree

22 files changed

+1157
-680
lines changed

22 files changed

+1157
-680
lines changed

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,13 @@ Contributions are welcome! If you are interested in contributing towards a new o
141141
Setup steps for working with the repository locally are documented [here](docs/Developing%20DistributedLock.md).
142142

143143
## Release notes
144+
- 2.5.1
145+
- 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)
146+
- 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)
147+
- 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)
148+
- Bump Oracle.ManagedDataAccess to latest to avoid bringing in vulnerable packages (DistributedLock.Core 1.0.8, DistributedLock.Oracle 1.0.4)
149+
- Bump Npgsql to latest patch to avoid bringing in vulnerable packages (DistributedLock.Postgres 1.2.1)
150+
- Improve directory creation concurrency handling for `FileDistributedLock` (DistributedLock.FileSystem 1.0.3)
144151
- 2.5
145152
- 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)
146153
- 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)
@@ -149,10 +156,10 @@ Setup steps for working with the repository locally are documented [here](docs/D
149156
- 2.4
150157
- 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)
151158
- 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!
152-
- Drop `net461` support (`net462` remains supported). Thanks [@Bartleby2718](https://github.com/Bartleby2718) for implementing!
159+
- Drop `net461` support (`net462` remains supported). Thanks [@Bartleby2718](https://github.com/Bartleby2718) for implementing!
153160
- Reduce occurrence of `UnobservedTaskException`s thrown by the library ([#192](https://github.com/madelson/DistributedLock/issues/192), DistributedLock.Core 1.0.6)
154161
- 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!
155-
- Improve directory creation concurrency handling for `FileDistributedLock` on Linux/.NET 8 ([#195](), DistributedLock.FileSystem 1.0.2)
162+
- Improve directory creation concurrency handling for `FileDistributedLock` on Linux/.NET 8 ([#195](https://github.com/madelson/DistributedLock/issues/195), DistributedLock.FileSystem 1.0.2)
156163
- 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)
157164
- New API documentation on [dndocs](https://dndocs.com/). Thanks [@NeuroXiq](https://github.com/NeuroXiq)!
158165
- New documentation for contributors to get the project running locally (see [Contributing](#contributing))

src/Directory.Packages.props

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
<PackageVersion Include="Nullable" Version="1.3.1" Condition="'$(TargetFramework)' != 'netstandard2.1'" />
1111
<PackageVersion Include="MySqlConnector" Version="2.3.5" />
1212
<PackageVersion Include="NUnit.Analyzers" Version="4.1.0" />
13-
<PackageVersion Include="Oracle.ManagedDataAccess.Core" Version="3.21.130" Condition="'$(TargetFramework)' == 'netstandard2.1'" />
14-
<PackageVersion Include="Oracle.ManagedDataAccess" Version="21.13.0" Condition="'$(TargetFramework)' == 'net462'" />
15-
<PackageVersion Include="Npgsql" Version="8.0.3" />
13+
<PackageVersion Include="Oracle.ManagedDataAccess.Core" Version="23.6.1" Condition="'$(TargetFramework)' == 'netstandard2.1'" />
14+
<PackageVersion Include="Oracle.ManagedDataAccess" Version="23.6.1" Condition="'$(TargetFramework)' == 'net472'" />
15+
<PackageVersion Include="Npgsql" Version="8.0.6" />
1616
<PackageVersion Include="StackExchange.Redis" Version="2.7.27" />
17-
<PackageVersion Include="Microsoft.Data.SqlClient" Version="5.2.1" />
17+
<PackageVersion Include="Microsoft.Data.SqlClient" Version="5.2.2" />
1818
<PackageVersion Include="nunit" Version="3.14.0" />
1919
<PackageVersion Include="nunit3testadapter" Version="4.5.0" />
2020
<PackageVersion Include="Microsoft.NET.Test.SDK" Version="17.9.0" />
@@ -26,5 +26,6 @@
2626
<PackageVersion Include="IsExternalInit" Version="1.0.3" />
2727
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" Condition="'$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'net462'" />
2828
<PackageVersion Include="System.ValueTuple" Version="4.5.0" Condition="'$(TargetFramework)' == 'net462'" />
29+
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
2930
</ItemGroup>
30-
</Project>
31+
</Project>

src/DistributedLock.Azure/AzureBlobLeaseDistributedLock.cs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace Medallion.Threading.Azure;
1212
public sealed partial class AzureBlobLeaseDistributedLock : IInternalDistributedLock<AzureBlobLeaseDistributedLockHandle>
1313
{
1414
/// <summary>
15-
/// Metadata marker used to indicate that a blob was created for distributed locking and therefore
15+
/// Metadata marker used to indicate that a blob was created for distributed locking and therefore
1616
/// should be destroyed upon release
1717
/// </summary>
1818
private static readonly string CreatedMetadataKey = $"__DistributedLock";
@@ -51,9 +51,9 @@ internal static string GetSafeName(string name, BlobContainerClient blobContaine
5151
{
5252
var maxLength = IsStorageEmulator() ? 256 : 1024;
5353

54-
return DistributedLockHelpers.ToSafeName(name, maxLength, s => ConvertToValidName(s));
54+
return DistributedLockHelpers.ToSafeName(name, maxLength, ConvertToValidName);
5555

56-
// check based on
56+
// check based on
5757
// https://docs.microsoft.com/en-us/azure/storage/common/storage-use-emulator#connect-to-the-emulator-account-using-the-well-known-account-name-and-key
5858
bool IsStorageEmulator() => blobContainerClient.Uri.IsAbsoluteUri
5959
&& blobContainerClient.Uri.AbsoluteUri.StartsWith("http://127.0.0.1:10000/devstoreaccount1", StringComparison.Ordinal);
@@ -105,13 +105,15 @@ static string ConvertToValidName(string name)
105105
);
106106

107107
private async ValueTask<AzureBlobLeaseDistributedLockHandle?> TryAcquireAsync(
108-
BlobLeaseClientWrapper leaseClient,
108+
BlobLeaseClientWrapper leaseClient,
109109
CancellationToken cancellationToken,
110110
bool isRetryAfterCreate)
111111
{
112-
try { await leaseClient.AcquireAsync(this._options.duration, cancellationToken).ConfigureAwait(false); }
113-
catch (RequestFailedException acquireException)
112+
using var response = await leaseClient.AcquireAsync(this._options.duration, cancellationToken).ConfigureAwait(false);
113+
if (response.IsError)
114114
{
115+
var acquireException = new RequestFailedException(response);
116+
115117
if (acquireException.ErrorCode == AzureErrors.LeaseAlreadyPresent) { return null; }
116118

117119
if (acquireException.ErrorCode == AzureErrors.BlobNotFound)
@@ -126,7 +128,7 @@ static string ConvertToValidName(string name)
126128
{
127129
// handle the race condition where we try to create and someone else creates it first
128130
return createException.ErrorCode == AzureErrors.LeaseIdMissing
129-
? default(AzureBlobLeaseDistributedLockHandle?)
131+
? default
130132
: throw new AggregateException($"Blob {this._blobClient.Name} does not exist and could not be created. See inner exceptions for details", acquireException, createException);
131133
}
132134

@@ -135,6 +137,11 @@ static string ConvertToValidName(string name)
135137
{
136138
// if the retry fails and we created, attempt deletion to clean things up
137139
try { await this._blobClient.DeleteIfExistsAsync().ConfigureAwait(false); }
140+
catch (RequestFailedException deletionException) when (deletionException.ErrorCode == AzureErrors.LeaseIdMissing)
141+
{
142+
// Handle the race condition where we try to delete and someone else acquired it:
143+
// in that case only the original Exception from TryAcquireAsync should be thrown.
144+
}
138145
catch (Exception deletionException)
139146
{
140147
throw new AggregateException(retryException, deletionException);
@@ -144,7 +151,7 @@ static string ConvertToValidName(string name)
144151
}
145152
}
146153

147-
throw;
154+
throw acquireException;
148155
}
149156

150157
var shouldDeleteBlob = isRetryAfterCreate
@@ -160,7 +167,7 @@ internal sealed class InternalHandle : IDistributedSynchronizationHandle, LeaseM
160167
private readonly bool _ownsBlob;
161168
private readonly AzureBlobLeaseDistributedLock _lock;
162169
private readonly LeaseMonitor _leaseMonitor;
163-
170+
164171
public InternalHandle(BlobLeaseClientWrapper leaseClient, bool ownsBlob, AzureBlobLeaseDistributedLock @lock)
165172
{
166173
this._leaseClient = leaseClient;
@@ -193,7 +200,7 @@ public async ValueTask DisposeAsync()
193200
{
194201
await this._lock._blobClient.DeleteIfExistsAsync(leaseId: this._leaseClient.LeaseId).ConfigureAwait(false);
195202
}
196-
else
203+
else
197204
{
198205
await this._leaseClient.ReleaseAsync().ConfigureAwait(false);
199206
}
Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,46 @@
1-
using Azure.Storage.Blobs.Specialized;
1+
using Azure;
2+
using Azure.Storage.Blobs.Specialized;
23
using Medallion.Threading.Internal;
34

45
namespace Medallion.Threading.Azure;
56

67
/// <summary>
78
/// Adds <see cref="SyncViaAsync"/> support to <see cref="BlobLeaseClient"/>
89
/// </summary>
9-
internal sealed class BlobLeaseClientWrapper
10+
internal sealed class BlobLeaseClientWrapper(BlobLeaseClient blobLeaseClient)
1011
{
11-
private readonly BlobLeaseClient _blobLeaseClient;
12+
public string LeaseId => blobLeaseClient.LeaseId;
1213

13-
public BlobLeaseClientWrapper(BlobLeaseClient blobLeaseClient)
14+
public ValueTask<Response> AcquireAsync(TimeoutValue duration, CancellationToken cancellationToken)
1415
{
15-
this._blobLeaseClient = blobLeaseClient;
16-
}
17-
18-
public string LeaseId => this._blobLeaseClient.LeaseId;
19-
20-
public ValueTask AcquireAsync(TimeoutValue duration, CancellationToken cancellationToken)
21-
{
22-
if (SyncViaAsync.IsSynchronous)
16+
RequestContext requestContext = new()
2317
{
24-
this._blobLeaseClient.Acquire(duration.TimeSpan, cancellationToken: cancellationToken);
25-
return default;
26-
}
27-
return new ValueTask(this._blobLeaseClient.AcquireAsync(duration.TimeSpan, cancellationToken: cancellationToken));
18+
CancellationToken = cancellationToken,
19+
ErrorOptions = ErrorOptions.NoThrow
20+
};
21+
22+
return SyncViaAsync.IsSynchronous
23+
? new ValueTask<Response>(blobLeaseClient.Acquire(duration.TimeSpan, conditions: null, requestContext))
24+
: new ValueTask<Response>(blobLeaseClient.AcquireAsync(duration.TimeSpan, conditions: null, requestContext));
2825
}
2926

3027
public ValueTask RenewAsync(CancellationToken cancellationToken)
3128
{
3229
if (SyncViaAsync.IsSynchronous)
3330
{
34-
this._blobLeaseClient.Renew(cancellationToken: cancellationToken);
31+
blobLeaseClient.Renew(cancellationToken: cancellationToken);
3532
return default;
3633
}
37-
return new ValueTask(this._blobLeaseClient.RenewAsync(cancellationToken: cancellationToken));
34+
return new ValueTask(blobLeaseClient.RenewAsync(cancellationToken: cancellationToken));
3835
}
3936

4037
public ValueTask ReleaseAsync()
4138
{
4239
if (SyncViaAsync.IsSynchronous)
4340
{
44-
this._blobLeaseClient.Release();
41+
blobLeaseClient.Release();
4542
return default;
4643
}
47-
return new ValueTask(this._blobLeaseClient.ReleaseAsync());
44+
return new ValueTask(blobLeaseClient.ReleaseAsync());
4845
}
49-
}
46+
}

src/DistributedLock.Azure/DistributedLock.Azure.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
</PropertyGroup>
1212

1313
<PropertyGroup>
14-
<Version>1.0.1</Version>
14+
<Version>1.0.2</Version>
1515
<AssemblyVersion>1.0.0.0</AssemblyVersion>
1616
<Authors>Michael Adelson</Authors>
1717
<Description>Provides a distributed locking implementation based on Azure blob leases</Description>

src/DistributedLock.Core/DistributedLock.Core.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
</PropertyGroup>
1212

1313
<PropertyGroup>
14-
<Version>1.0.7</Version>
14+
<Version>1.0.8</Version>
1515
<AssemblyVersion>1.0.0.0</AssemblyVersion>
1616
<Authors>Michael Adelson</Authors>
1717
<Description>Core interfaces and utilities that support the DistributedLock.* family of packages</Description>

src/DistributedLock.Core/Internal/Data/DatabaseConnection.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,12 @@ public async ValueTask OpenAsync(CancellationToken cancellationToken)
8282
if ((cancellationToken.CanBeCanceled || !SyncViaAsync.IsSynchronous)
8383
&& this.InnerConnection is DbConnection dbConnection)
8484
{
85-
await dbConnection.OpenAsync(cancellationToken).ConfigureAwait(false);
85+
try { await dbConnection.OpenAsync(cancellationToken).ConfigureAwait(false); }
86+
// Oracle can throw OracleException instead of OCE here
87+
catch (Exception ex) when (cancellationToken.IsCancellationRequested && this.IsCommandCancellationException(ex))
88+
{
89+
throw new OperationCanceledException("Connection open canceled", ex, cancellationToken);
90+
}
8691
}
8792
else
8893
{

src/DistributedLock.FileSystem/DistributedLock.FileSystem.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
</PropertyGroup>
1212

1313
<PropertyGroup>
14-
<Version>1.0.2</Version>
14+
<Version>1.0.3</Version>
1515
<AssemblyVersion>1.0.0.0</AssemblyVersion>
1616
<Authors>Michael Adelson</Authors>
1717
<Description>Provides a distributed lock implementation based on file locks</Description>

src/DistributedLock.FileSystem/FileDistributedLock.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public sealed partial class FileDistributedLock : IInternalDistributedLock<FileD
1212
/// before we assume that the issue is non-transient. Empirically I've found this value to be reliable both locally and on AppVeyor (if there
1313
/// IS a problem there's little risk to trying more times because we'll eventually be failing hard).
1414
/// </summary>
15-
private const int MaxUnauthorizedAccessExceptionRetries = 800;
15+
private const int MaxUnauthorizedAccessExceptionRetries = 1600;
1616

1717
// These are not configurable currently because in the future we may want to change the implementation of FileDistributedLock
1818
// to leverage native methods which may allow for actual blocking. The values here reflect the idea that we expect file locks

src/DistributedLock.Oracle/DistributedLock.Oracle.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard2.1;net462</TargetFrameworks>
4+
<TargetFrameworks>netstandard2.1;net472</TargetFrameworks>
55
<RootNamespace>Medallion.Threading.Oracle</RootNamespace>
66
<GenerateDocumentationFile>True</GenerateDocumentationFile>
77
<WarningLevel>4</WarningLevel>
@@ -11,7 +11,7 @@
1111
</PropertyGroup>
1212

1313
<PropertyGroup>
14-
<Version>1.0.3</Version>
14+
<Version>1.0.4</Version>
1515
<AssemblyVersion>1.0.0.0</AssemblyVersion>
1616
<Authors>Michael Adelson</Authors>
1717
<Description>Provides a distributed lock implementation based on Oracle Database</Description>
@@ -46,7 +46,7 @@
4646

4747
<ItemGroup>
4848
<PackageReference Include="Oracle.ManagedDataAccess.Core" Condition="'$(TargetFramework)' == 'netstandard2.1'" />
49-
<PackageReference Include="Oracle.ManagedDataAccess" Condition="'$(TargetFramework)' == 'net462'"/>
49+
<PackageReference Include="Oracle.ManagedDataAccess" Condition="'$(TargetFramework)' == 'net472'"/>
5050
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
5151
<PackageReference Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" PrivateAssets="All" />
5252
</ItemGroup>

0 commit comments

Comments
 (0)