Handle missing distributed lock provider in readiness check

This commit is contained in:
Sipke Schoorstra 2026-05-20 23:39:35 +02:00
parent d6903fdb76
commit 9cdff8c318
No known key found for this signature in database
GPG key ID: 5C10502B28A4268F
2 changed files with 34 additions and 2 deletions

View file

@ -1,5 +1,6 @@
using Elsa.Common;
using Medallion.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace Elsa.Workflows.Runtime.HealthChecks;
@ -7,7 +8,7 @@ namespace Elsa.Workflows.Runtime.HealthChecks;
/// <summary>
/// Verifies that the configured distributed lock provider can acquire and release a probe lock.
/// </summary>
public class ElsaDistributedLockHealthCheck(IDistributedLockProvider distributedLockProvider) : IHealthCheck
public class ElsaDistributedLockHealthCheck(IServiceProvider serviceProvider) : IHealthCheck
{
private static readonly string LockName = $"elsa-health-check-{Environment.MachineName}-{Guid.NewGuid():N}";
private static readonly TimeSpan LockAcquisitionTimeout = TimeSpan.FromSeconds(1);
@ -17,6 +18,15 @@ public class ElsaDistributedLockHealthCheck(IDistributedLockProvider distributed
{
try
{
var distributedLockProvider = serviceProvider.GetService<IDistributedLockProvider>();
if (distributedLockProvider == null)
{
return HealthCheckResult.Degraded("Elsa distributed lock provider is not registered.", data: new Dictionary<string, object>
{
["category"] = "distributed-locks"
});
}
await using var handle = await distributedLockProvider.TryAcquireLockAsync(LockName, LockAcquisitionTimeout, cancellationToken);
if (handle == null)
{

View file

@ -1,5 +1,6 @@
using Elsa.Workflows.Runtime.HealthChecks;
using Medallion.Threading;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using NSubstitute;
@ -17,7 +18,7 @@ public class ElsaDistributedLockHealthCheckTests
_distributedLockProvider.CreateLock(Arg.Any<string>()).Returns(_distributedLock);
_distributedLock.TryAcquireAsync(Arg.Any<TimeSpan>(), Arg.Any<CancellationToken>())
.Returns(new ValueTask<IDistributedSynchronizationHandle?>(Substitute.For<IDistributedSynchronizationHandle>()));
_sut = new ElsaDistributedLockHealthCheck(_distributedLockProvider);
_sut = new ElsaDistributedLockHealthCheck(CreateServiceProvider(_distributedLockProvider));
}
[Fact]
@ -53,4 +54,25 @@ public class ElsaDistributedLockHealthCheckTests
Assert.Equal(HealthStatus.Unhealthy, result.Status);
Assert.Equal("distributed-locks", result.Data["category"]);
}
[Fact]
public async Task ReturnsDegradedWhenProviderIsNotRegistered()
{
var sut = new ElsaDistributedLockHealthCheck(CreateServiceProvider());
var result = await sut.CheckHealthAsync(new HealthCheckContext());
Assert.Equal(HealthStatus.Degraded, result.Status);
Assert.Equal("distributed-locks", result.Data["category"]);
}
private static IServiceProvider CreateServiceProvider(IDistributedLockProvider? distributedLockProvider = null)
{
var services = new ServiceCollection();
if (distributedLockProvider != null)
services.AddSingleton(distributedLockProvider);
return services.BuildServiceProvider();
}
}