mirror of https://github.com/Squidex/squidex.git
4 changed files with 208 additions and 2 deletions
@ -0,0 +1,77 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.Diagnostics.HealthChecks; |
||||
|
using Orleans; |
||||
|
using Squidex.Infrastructure.EventSourcing.Grains; |
||||
|
using Squidex.Infrastructure.Orleans; |
||||
|
|
||||
|
namespace Squidex.Infrastructure.EventSourcing |
||||
|
{ |
||||
|
public sealed class EventConsumersHealthCheck : IHealthCheck |
||||
|
{ |
||||
|
private readonly IGrainFactory grainFactory; |
||||
|
|
||||
|
public EventConsumersHealthCheck(IGrainFactory grainFactory) |
||||
|
{ |
||||
|
Guard.NotNull(grainFactory); |
||||
|
|
||||
|
this.grainFactory = grainFactory; |
||||
|
} |
||||
|
|
||||
|
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
var eventConsumers = await GetGrain().GetConsumersAsync(); |
||||
|
|
||||
|
var data = new Dictionary<string, object>(); |
||||
|
|
||||
|
var numTotal = 0; |
||||
|
var numFailed = 0; |
||||
|
|
||||
|
foreach (var eventConsumer in eventConsumers.Value) |
||||
|
{ |
||||
|
var status = "Running"; |
||||
|
|
||||
|
if (eventConsumer.Error != null) |
||||
|
{ |
||||
|
status = "Failed"; |
||||
|
|
||||
|
numFailed++; |
||||
|
} |
||||
|
else if (eventConsumer.IsStopped) |
||||
|
{ |
||||
|
status = "Stopped"; |
||||
|
} |
||||
|
|
||||
|
data[eventConsumer.Name] = status; |
||||
|
|
||||
|
numTotal++; |
||||
|
} |
||||
|
|
||||
|
if (numTotal > 0 && numFailed == numTotal) |
||||
|
{ |
||||
|
return HealthCheckResult.Unhealthy("All event consumers failed", null, data); |
||||
|
} |
||||
|
else if (numFailed > 0) |
||||
|
{ |
||||
|
return HealthCheckResult.Degraded("One or more event consumers failed", null, data); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return HealthCheckResult.Healthy(data: data); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private IEventConsumerManagerGrain GetGrain() |
||||
|
{ |
||||
|
return grainFactory.GetGrain<IEventConsumerManagerGrain>(SingleGrain.Id); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,121 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using FakeItEasy; |
||||
|
using Microsoft.Extensions.Diagnostics.HealthChecks; |
||||
|
using Orleans; |
||||
|
using Orleans.Concurrency; |
||||
|
using Squidex.Infrastructure.EventSourcing.Grains; |
||||
|
using Squidex.Infrastructure.Orleans; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Squidex.Infrastructure.EventSourcing |
||||
|
{ |
||||
|
public class EventConsumersHealthCheckTests |
||||
|
{ |
||||
|
private readonly IGrainFactory grainFactory = A.Fake<IGrainFactory>(); |
||||
|
private readonly IEventConsumerManagerGrain eventConsumerManager = A.Fake<IEventConsumerManagerGrain>(); |
||||
|
private readonly List<EventConsumerInfo> consumers = new List<EventConsumerInfo>(); |
||||
|
private readonly EventConsumersHealthCheck sut; |
||||
|
|
||||
|
public EventConsumersHealthCheckTests() |
||||
|
{ |
||||
|
A.CallTo(() => grainFactory.GetGrain<IEventConsumerManagerGrain>(SingleGrain.Id, null)) |
||||
|
.Returns(eventConsumerManager); |
||||
|
|
||||
|
A.CallTo(() => eventConsumerManager.GetConsumersAsync()) |
||||
|
.Returns(consumers.AsImmutable()); |
||||
|
|
||||
|
sut = new EventConsumersHealthCheck(grainFactory); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_return_healthy_if_no_consumer_found() |
||||
|
{ |
||||
|
var status = await sut.CheckHealthAsync(null!); |
||||
|
|
||||
|
Assert.Equal(HealthStatus.Healthy, status.Status); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_return_healthy_if_no_consumer_failed() |
||||
|
{ |
||||
|
consumers.Add(new EventConsumerInfo |
||||
|
{ |
||||
|
Name = "Consumer1", |
||||
|
}); |
||||
|
|
||||
|
consumers.Add(new EventConsumerInfo |
||||
|
{ |
||||
|
Name = "Consumer2" |
||||
|
}); |
||||
|
|
||||
|
consumers.Add(new EventConsumerInfo |
||||
|
{ |
||||
|
Name = "Consumer2" |
||||
|
}); |
||||
|
|
||||
|
var status = await sut.CheckHealthAsync(null!); |
||||
|
|
||||
|
Assert.Equal(HealthStatus.Healthy, status.Status); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_return_unhealthy_if_all_consumers_failed() |
||||
|
{ |
||||
|
consumers.Add(new EventConsumerInfo |
||||
|
{ |
||||
|
Name = "Consumer1", |
||||
|
Error = "Failed1" |
||||
|
}); |
||||
|
|
||||
|
consumers.Add(new EventConsumerInfo |
||||
|
{ |
||||
|
Name = "Consumer2", |
||||
|
Error = "Failed2" |
||||
|
}); |
||||
|
|
||||
|
consumers.Add(new EventConsumerInfo |
||||
|
{ |
||||
|
Name = "Consumer3", |
||||
|
Error = "Failed3" |
||||
|
}); |
||||
|
|
||||
|
var status = await sut.CheckHealthAsync(null!); |
||||
|
|
||||
|
Assert.Equal(HealthStatus.Unhealthy, status.Status); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_return_degrated_if_at_least_one_consumers_failed() |
||||
|
{ |
||||
|
consumers.Add(new EventConsumerInfo |
||||
|
{ |
||||
|
Name = "Consumer1", |
||||
|
Error = "Failed1" |
||||
|
}); |
||||
|
|
||||
|
consumers.Add(new EventConsumerInfo |
||||
|
{ |
||||
|
Name = "Consumer2", |
||||
|
IsStopped = true |
||||
|
}); |
||||
|
|
||||
|
consumers.Add(new EventConsumerInfo |
||||
|
{ |
||||
|
Name = "Consumer3", |
||||
|
IsStopped = false |
||||
|
}); |
||||
|
|
||||
|
var status = await sut.CheckHealthAsync(null!); |
||||
|
|
||||
|
Assert.Equal(HealthStatus.Degraded, status.Status); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue