Skip to content

Journal health checks not registered when no event adapters are configured #666

@Aaronontheweb

Description

@Aaronontheweb

Description

Journal health checks are not registered when using .WithHealthCheck() without adding event adapters. This is due to an early return in AkkaPersistenceJournalBuilder.Build() that exits before health check registration occurs.

Reproduction

protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider)
{
    var journalOptions = new SqlJournalOptions(true)
    {
        ConnectionString = "...",
        ProviderName = ProviderName.SQLite
    };

    // ❌ Health check NOT registered - no event adapters added
    builder.WithJournal(
        journalOptions,
        journal => journal.WithHealthCheck());
}

// Result: No journal health check appears in HealthCheckService

Expected Behavior

Health checks should be registered regardless of whether event adapters are configured. These are independent features.

Actual Behavior

Health check is silently ignored. Only appears if you also add an event adapter:

// ✅ Health check IS registered - event adapter added
builder.WithJournal(
    journalOptions,
    journal => journal
        .AddWriteEventAdapter<MyAdapter>("adapter", [typeof(MyEvent)])
        .WithHealthCheck());

Root Cause

File: src/Akka.Persistence.Hosting/AkkaPersistenceHostingExtensions.cs
Method: AkkaPersistenceJournalBuilder.Build() (lines 113-133)

internal void Build()
{
    // useless configuration - don't bother.
    if (Adapters.Count == 0 || Bindings.Count == 0)
        return;  // ← EXITS EARLY, skips health check registration!

    var adapters = new StringBuilder()
        .Append($"akka.persistence.journal.{JournalId}").Append("{");

    AppendAdapters(adapters);

    adapters.AppendLine("}");

    var finalHocon = ConfigurationFactory.ParseString(adapters.ToString())
        .WithFallback(Persistence.DefaultConfig());
    Builder.AddHocon(finalHocon, HoconAddMode.Prepend);
    
    // add the health checks if specified
    if(HealthCheckRegistration != null)
        Builder.WithHealthCheck(HealthCheckRegistration);  // ← NEVER REACHED!
}

The early return at line 116 exits before health check registration at line 132.

Proposed Fix

Move health check registration before the early return:

internal void Build()
{
    // add the health checks if specified - do this FIRST
    if(HealthCheckRegistration != null)
        Builder.WithHealthCheck(HealthCheckRegistration);
    
    // useless configuration - don't bother with adapters if none configured
    if (Adapters.Count == 0 || Bindings.Count == 0)
        return;
    
    var adapters = new StringBuilder()
        .Append($"akka.persistence.journal.{JournalId}").Append("{");

    AppendAdapters(adapters);

    adapters.AppendLine("}");

    var finalHocon = ConfigurationFactory.ParseString(adapters.ToString())
        .WithFallback(Persistence.DefaultConfig());
    Builder.AddHocon(finalHocon, HoconAddMode.Prepend);
}

This ensures health checks are registered even when no event adapters are configured.

Impact

Medium-High: Users expecting health checks to work will find they're silently not registered unless they also happen to configure event adapters. This creates a confusing and non-obvious dependency between two unrelated features.

Workaround

Always add at least one event adapter (even a no-op) when using health checks:

builder.WithJournal(
    journalOptions,
    journal => journal
        .AddWriteEventAdapter<IdentityAdapter>("noop", [])  // Workaround
        .WithHealthCheck());

Note on Snapshot Stores

This bug does NOT affect snapshot stores because AkkaPersistenceSnapshotBuilder.Build() doesn't have the same early return pattern - it always registers health checks.

Discovery

This issue was discovered while refactoring Akka.Persistence.Sql.Hosting to use the unified API in PR akkadotnet/Akka.Persistence.Sql#549. The refactoring exposed this edge case where health checks are registered for snapshot stores but not journals when no event adapters are present.

Test Coverage

The existing test JournalAndSnapshotWithBuildersSpec (UnifiedApiSpecs.cs:224-286) doesn't catch this bug because it adds an event adapter before calling .WithHealthCheck():

journal => journal
    .AddWriteEventAdapter<UnifiedApiTestResources.TestWriteAdapter>("adapter",
        [typeof(UnifiedApiTestResources.TestEvent)])
    .WithHealthCheck(),  // Works because adapter was added first

Recommended test: Add a test that calls .WithHealthCheck() WITHOUT adding event adapters to ensure health checks work independently.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions