38 lines
1.4 KiB
C#
38 lines
1.4 KiB
C#
|
|
using System.Collections.Concurrent;
|
||
|
|
|
||
|
|
namespace w4c_workflows.Services;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// In-memory lease service for lite/self-hosted mode where Redis is not available.
|
||
|
|
/// Uses a ConcurrentDictionary with TTL tracking. Not distributed — only works
|
||
|
|
/// for a single-process deployment (which is the lite mode use case).
|
||
|
|
/// </summary>
|
||
|
|
public sealed class InMemoryLeaseService : ILeaseService
|
||
|
|
{
|
||
|
|
private record Lease(string Owner, DateTime ExpiresAt);
|
||
|
|
|
||
|
|
private readonly ConcurrentDictionary<string, Lease> _leases = new();
|
||
|
|
|
||
|
|
public Task<bool> AcquireAsync(string tenantId, string runId, string owner, TimeSpan ttl)
|
||
|
|
{
|
||
|
|
var key = $"{tenantId}:{runId}";
|
||
|
|
var lease = new Lease(owner, DateTime.UtcNow + ttl);
|
||
|
|
return Task.FromResult(_leases.TryAdd(key, lease));
|
||
|
|
}
|
||
|
|
|
||
|
|
public Task<bool> RenewAsync(string tenantId, string runId, string owner, TimeSpan ttl)
|
||
|
|
{
|
||
|
|
var key = $"{tenantId}:{runId}";
|
||
|
|
if (!_leases.TryGetValue(key, out var current) || current.Owner != owner)
|
||
|
|
return Task.FromResult(false);
|
||
|
|
_leases[key] = new Lease(owner, DateTime.UtcNow + ttl);
|
||
|
|
return Task.FromResult(true);
|
||
|
|
}
|
||
|
|
|
||
|
|
public Task<bool> ReleaseAsync(string tenantId, string runId, string owner)
|
||
|
|
{
|
||
|
|
var key = $"{tenantId}:{runId}";
|
||
|
|
// Remove only if the owner still matches (compare-and-delete).
|
||
|
|
return Task.FromResult(_leases.TryRemove(key, out var lease) && lease.Owner == owner);
|
||
|
|
}
|
||
|
|
}
|