-
Notifications
You must be signed in to change notification settings - Fork 23
Adding Akka.Persistence health checks #662
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
ef0160b
c057da2
4ea9841
4be1301
264db74
dab033c
772060d
d3aa948
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |
| using Akka.Persistence.Journal; | ||
| using Akka.Util; | ||
| using Akka.Actor; | ||
| using Microsoft.Extensions.Diagnostics.HealthChecks; | ||
|
|
||
| #nullable enable | ||
| namespace Akka.Persistence.Hosting | ||
|
|
@@ -37,13 +38,29 @@ public sealed class AkkaPersistenceJournalBuilder | |
| internal readonly AkkaConfigurationBuilder Builder; | ||
| internal readonly Dictionary<Type, HashSet<string>> Bindings = new Dictionary<Type, HashSet<string>>(); | ||
| internal readonly Dictionary<string, Type> Adapters = new Dictionary<string, Type>(); | ||
| internal AkkaHealthCheckRegistration? HealthCheckRegistration = null; | ||
|
|
||
| public AkkaPersistenceJournalBuilder(string journalId, AkkaConfigurationBuilder builder) | ||
| { | ||
| JournalId = journalId; | ||
| Builder = builder; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Uses the built-in journal health check on the Akka.Persistence.Journal. | ||
| /// </summary> | ||
| /// <param name="unHealthyStatus">Default status to return when the plugin reports <see cref="PersistenceHealthStatus.Unhealthy"/> | ||
| /// or <see cref="PersistenceHealthStatus.Degraded"/>. Defaults to degraded.</param> | ||
| /// <param name="name">Optional name to add to the health check.</param> | ||
| /// <returns></returns> | ||
| public AkkaPersistenceJournalBuilder WithHealthCheck(HealthStatus unHealthyStatus = HealthStatus.Degraded, | ||
| string? name = null) | ||
| { | ||
| var registration = AddHealthCheck(name, unHealthyStatus); | ||
| HealthCheckRegistration = registration; | ||
| return this; | ||
| } | ||
|
|
||
| public AkkaPersistenceJournalBuilder AddEventAdapter<TAdapter>(string eventAdapterName, | ||
| IEnumerable<Type> boundTypes) where TAdapter : IEventAdapter | ||
| { | ||
|
|
@@ -79,6 +96,16 @@ private void AddAdapter<TAdapter>(string eventAdapterName, IEnumerable<Type> bou | |
| } | ||
| } | ||
|
|
||
| private AkkaHealthCheckRegistration AddHealthCheck(string? name, HealthStatus unHealthyStatus) | ||
| { | ||
| var registration = new AkkaHealthCheckRegistration( | ||
| name ?? $"Akka.Persistence.Journal.{JournalId}", | ||
| new JournalHealthCheck(JournalId), | ||
| unHealthyStatus, | ||
| ["akka", "persistence", "journal"]); | ||
| return registration; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// INTERNAL API - Builds the HOCON and then injects it. | ||
| /// </summary> | ||
|
|
@@ -98,6 +125,10 @@ internal void Build() | |
| var finalHocon = ConfigurationFactory.ParseString(adapters.ToString()) | ||
| .WithFallback(Persistence.DefaultConfig()); // add the default config as a fallback | ||
| Builder.AddHocon(finalHocon, HoconAddMode.Prepend); | ||
|
|
||
| // add the health checks if specified | ||
| if(HealthCheckRegistration != null) | ||
| Builder.WithHealthCheck(HealthCheckRegistration); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add the health checks to the |
||
| } | ||
|
|
||
| internal void AppendAdapters(StringBuilder sb) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Akka.Hosting; | ||
| using Microsoft.Extensions.Diagnostics.HealthChecks; | ||
|
|
||
| namespace Akka.Persistence.Hosting; | ||
|
|
||
| internal static class HealthCheckExt | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maps the Akka.Persistence health check data structures into Microsoft.Extensions.HealthChecks data structures |
||
| { | ||
| public static HealthCheckResult ToHealthCheckResult(this PersistenceHealthCheckResult persistenceHealthStatus) | ||
| => new(persistenceHealthStatus.Status.ToHealthStatus(), persistenceHealthStatus.Description, | ||
| persistenceHealthStatus.Exception, persistenceHealthStatus.Data); | ||
|
|
||
| public static HealthStatus ToHealthStatus(this PersistenceHealthStatus persistenceHealthStatus) => persistenceHealthStatus switch | ||
| { | ||
| PersistenceHealthStatus.Healthy => HealthStatus.Healthy, | ||
| PersistenceHealthStatus.Degraded => HealthStatus.Degraded, | ||
| _ => HealthStatus.Unhealthy | ||
| }; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// INTERNAL API | ||
| /// | ||
| /// Leverages internal Akka.Persistence APIs to perform a health check on a journal. | ||
| /// </summary> | ||
| internal sealed class JournalHealthCheck : IAkkaHealthCheck | ||
| { | ||
| private readonly string _journalPluginId; | ||
|
|
||
| public JournalHealthCheck(string journalPluginId) | ||
| { | ||
| _journalPluginId = journalPluginId; | ||
| } | ||
|
|
||
| public async Task<HealthCheckResult> CheckHealthAsync(AkkaHealthCheckContext context, CancellationToken cancellationToken = default) | ||
| { | ||
| var persistence = Persistence.Instance.Apply(context.ActorSystem); | ||
| var journalResult = await persistence.CheckJournalHealthAsync(_journalPluginId, cancellationToken); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Runs the health checks we added in akkadotnet/akka.net#7842 and pipes them through the MSFT.EXT.HealthCheck functionality |
||
| return journalResult.ToHealthCheckResult(); | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// INTERNAL API | ||
| /// | ||
| /// Leverages internal Akka.Persistence APIs to perform a health check on the snapshot store. | ||
| /// </summary> | ||
| internal sealed class SnapshotStoreHealthCheck : IAkkaHealthCheck | ||
| { | ||
| private readonly string _snapshotStorePluginId; | ||
|
|
||
| public SnapshotStoreHealthCheck(string snapshotStorePluginId) | ||
| { | ||
| _snapshotStorePluginId = snapshotStorePluginId; | ||
| } | ||
|
|
||
| public async Task<HealthCheckResult> CheckHealthAsync(AkkaHealthCheckContext context, CancellationToken cancellationToken = default) | ||
| { | ||
| var persistence = Persistence.Instance.Apply(context.ActorSystem); | ||
| var ssResult = await persistence.CheckSnapshotStoreHealthAsync(_snapshotStorePluginId, cancellationToken); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the same as above, but for snapshots. |
||
| return ssResult.ToHealthCheckResult(); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Registers the Akka.Persistence health check definition is inside the
AsyncWriteJournal- by default it just looks as the state of theCircuitBreaker, which is a "good enough" heuristic typically.