Skip to content

Commit 3ded4de

Browse files
committed
Add distributed LockAsync support to cache interfaces
Implemented LockAsync in IAdvancedDistributedCache, AdvancedMemoryCache, and AdvancedRedisCache to provide distributed locking with expiration. Updated documentation and bumped version to 1.0.21.
1 parent a99c855 commit 3ded4de

4 files changed

Lines changed: 66 additions & 5 deletions

File tree

src/IAdvancedDistributedCache.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,5 +157,18 @@ public interface IAdvancedDistributedCache : IDistributedCache, IDisposable
157157
/// <param name="token">Contains an optional cancellation token.</param>
158158
/// <returns>Returns a value indicating success.</returns>
159159
Task<bool> HashFieldsExpireAsync(string hashKey, string[] fieldNames, TimeSpan expiration, CancellationToken token = default);
160+
161+
/// <summary>
162+
/// Attempts to acquire a distributed lock for the specified key with a given expiration time.
163+
/// </summary>
164+
/// <remarks>If the lock is already held by another process, the method returns <see
165+
/// langword="false"/> immediately without waiting. The lock will be automatically released after the specified
166+
/// expiration time if not released earlier.</remarks>
167+
/// <param name="key">The unique identifier for the lock to acquire. Cannot be null or empty.</param>
168+
/// <param name="expirationTime">The duration for which the lock will be held before it expires automatically.</param>
169+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the lock acquisition operation.</param>
170+
/// <returns>A task that represents the asynchronous operation. The task result is <see langword="true"/> if the lock was
171+
/// successfully acquired; otherwise, <see langword="false"/>.</returns>
172+
Task<bool> LockAsync(string key, TimeSpan expirationTime, CancellationToken cancellationToken = default);
160173
}
161174
}

src/Memory/AdvancedMemoryCache.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,31 @@ public async Task<bool> HashFieldsExpireAsync(string hashKey, string[] fieldName
422422
return await Task.FromResult(result);
423423
}
424424

425+
/// <summary>
426+
/// Attempts to acquire a lock for the specified key with a given expiration time, using an asynchronous
427+
/// operation.
428+
/// </summary>
429+
/// <param name="key">The unique identifier for the lock to acquire. Cannot be null.</param>
430+
/// <param name="expirationTime">The duration for which the lock should be held before it expires.</param>
431+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchronous operation.</param>
432+
/// <returns>A task that represents the asynchronous operation. The task result is <see langword="true"/> if the lock was
433+
/// successfully acquired; otherwise, <see langword="false"/>.</returns>
434+
/// <exception cref="ArgumentNullException">Thrown if <paramref name="key"/> is null.</exception>
435+
public async Task<bool> LockAsync(string key, TimeSpan expirationTime, CancellationToken cancellationToken = default)
436+
{
437+
bool result = false;
438+
if (key == null)
439+
{
440+
throw new ArgumentNullException(nameof(key));
441+
}
442+
if (!memoryDictionary.ContainsKey(key))
443+
{
444+
memoryDictionary[key] = "LOCKED";
445+
result = true;
446+
}
447+
return await Task.FromResult(result);
448+
}
449+
425450
#region IDisposeable Methods
426451

427452
/// <summary>

src/Redis/AdvancedRedisCache.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ public async Task<bool> HashSetAsync(string hashKey, string fieldName, string va
591591
token.ThrowIfCancellationRequested();
592592
await this.Cache.KeyExpireAsync(hashKey, expiration);
593593
}
594-
594+
595595
return result;
596596
}
597597

@@ -720,13 +720,36 @@ public async Task<bool> HashFieldsExpireAsync(string hashKey, string[] fieldName
720720

721721
return result;
722722
}
723+
724+
/// <summary>
725+
/// Attempts to acquire a distributed lock for the specified key with a given expiration time.
726+
/// </summary>
727+
/// <remarks>If the lock is already held by another process, the method returns <see
728+
/// langword="false"/> immediately without waiting. The lock will be automatically released after the specified
729+
/// expiration time if not released earlier.</remarks>
730+
/// <param name="key">The unique identifier for the lock to acquire. Cannot be null or empty.</param>
731+
/// <param name="expirationTime">The duration for which the lock will be held before it expires automatically.</param>
732+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the lock acquisition operation.</param>
733+
/// <returns>A task that represents the asynchronous operation. The task result is <see langword="true"/> if the lock was
734+
/// successfully acquired; otherwise, <see langword="false"/>.</returns>
735+
public async Task<bool> LockAsync(string key, TimeSpan expirationTime, CancellationToken cancellationToken = default)
736+
{
737+
if (string.IsNullOrEmpty(key))
738+
{
739+
throw new ArgumentNullException(nameof(key));
740+
}
741+
await this.ConnectAsync(cancellationToken);
742+
cancellationToken.ThrowIfCancellationRequested();
743+
var result = await this.Cache.ExecuteAsync("SET", this.instance + key, "1", "NX", "PX", (long)expirationTime.TotalMilliseconds);
744+
return result.ToString() == "OK";
745+
}
723746
#endregion
724747

725748
#region IDisposeable Methods
726749

727-
/// <summary>
728-
/// This method is used to dispose of the internal disposable objects.
729-
/// </summary>
750+
/// <summary>
751+
/// This method is used to dispose of the internal disposable objects.
752+
/// </summary>
730753
public void Dispose()
731754
{
732755
Dispose(true);

src/Talegen.AspNetCore.AdvancedCache.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<ApplicationIcon>Assets\logo.ico</ApplicationIcon>
77
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
88
<Title>Talegen ASP.net Core Advanced Cache Library</Title>
9-
<Version>1.0.20</Version>
9+
<Version>1.0.21</Version>
1010
<Authors>Talegen, LLC</Authors>
1111
<Description>A simple advanced IDistributedCache extension implementing Redis cache client.</Description>
1212
<Copyright>Copyright (c) Talegen, LLC.</Copyright>

0 commit comments

Comments
 (0)