Try your search with a different keyword or use * as a wildcard.
using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json;
namespace Nop.Core.Caching;
public partial class DistributedCacheLocker : ILocker
{
#region Fields
protected static readonly string _running = JsonConvert.SerializeObject(TaskStatus.Running);
protected readonly IDistributedCache _distributedCache;
#endregion
#region Ctor
public DistributedCacheLocker(IDistributedCache distributedCache)
{
_distributedCache = distributedCache;
}
#endregion
#region Methods
///
/// Performs some asynchronous task with exclusive lock
///
/// The key we are locking on
/// The time after which the lock will automatically be expired
/// Asynchronous task to be performed with locking
/// A task that resolves true if lock was acquired and action was performed; otherwise false
public async Task PerformActionWithLockAsync(string resource, TimeSpan expirationTime, Func action)
{
//ensure that lock is acquired
if (!string.IsNullOrEmpty(await _distributedCache.GetStringAsync(resource)))
return false;
try
{
await _distributedCache.SetStringAsync(resource, resource, new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = expirationTime
});
await action();
return true;
}
finally
{
//release lock even if action fails
await _distributedCache.RemoveAsync(resource);
}
}
///
/// Starts a background task with "heartbeat": a status flag that will be periodically updated to signal to
/// others that the task is running and stop them from starting the same task.
///
/// The key of the background task
/// The time after which the heartbeat key will automatically be expired. Should be longer than
/// The interval at which to update the heartbeat, if required by the implementation
/// Asynchronous background task to be performed
/// A CancellationTokenSource for manually canceling the task
/// A task that resolves true if lock was acquired and action was performed; otherwise false
public async Task RunWithHeartbeatAsync(string key, TimeSpan expirationTime, TimeSpan heartbeatInterval, Func action, CancellationTokenSource cancellationTokenSource = default)
{
if (!string.IsNullOrEmpty(await _distributedCache.GetStringAsync(key)))
return;
var tokenSource = cancellationTokenSource ?? new CancellationTokenSource();
try
{
// run heartbeat early to minimize risk of multiple execution
await _distributedCache.SetStringAsync(
key,
_running,
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = expirationTime },
token: tokenSource.Token);
await using var timer = new Timer(
callback: _ =>
{
try
{
tokenSource.Token.ThrowIfCancellationRequested();
var status = _distributedCache.GetString(key);
if (!string.IsNullOrEmpty(status) && JsonConvert.DeserializeObject(status) ==
TaskStatus.Canceled)
{
tokenSource.Cancel();
return;
}
_distributedCache.SetString(
key,
_running,
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = expirationTime });
}
catch (OperationCanceledException) { }
},
state: null,
dueTime: 0,
period: (int)heartbeatInterval.TotalMilliseconds);
await action(tokenSource.Token);
}
catch (OperationCanceledException) { }
finally
{
await _distributedCache.RemoveAsync(key);
}
}
///
/// Tries to cancel a background task by flagging it for cancellation on the next heartbeat.
///
/// The task's key
/// The time after which the task will be considered stopped due to system shutdown or other causes,
/// even if not explicitly canceled.
/// A task that represents requesting cancellation of the task. Note that the completion of this task does not
/// necessarily imply that the task has been canceled, only that cancellation has been requested.
public async Task CancelTaskAsync(string key, TimeSpan expirationTime)
{
var status = await _distributedCache.GetStringAsync(key);
if (!string.IsNullOrEmpty(status) &&
JsonConvert.DeserializeObject(status) != TaskStatus.Canceled)
await _distributedCache.SetStringAsync(
key,
JsonConvert.SerializeObject(TaskStatus.Canceled),
new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = expirationTime });
}
///
/// Check if a background task is running.
///
/// The task's key
/// A task that resolves to true if the background task is running; otherwise false
public async Task IsTaskRunningAsync(string key)
{
return !string.IsNullOrEmpty(await _distributedCache.GetStringAsync(key));
}
#endregion
}