Skip to content

Conversation

@Aaronontheweb
Copy link
Member

Summary

Fixed a critical bug where event adapters configured via the NEW callback API were not being invoked at runtime, despite being correctly present in HOCON configuration.

Root Cause

AkkaPersistenceJournalBuilder.Build() was creating a Config with .WithFallback(Persistence.DefaultConfig()) which interfered with the main journal configuration during HOCON merging, causing adapters to be registered in config but not invoked during persistence operations.

File: src/Akka.Persistence.Hosting/AkkaPersistenceHostingExtensions.cs line 130

The Fix

Removed the unnecessary .WithFallback(Persistence.DefaultConfig()) call. The adapter configuration blocks (event-adapters and event-adapter-bindings) don't require fallback to default persistence config and merge correctly with the journal config without it.

Impact

This bug affected users with:

  • Multiple journal configurations (e.g., default + sharding journals)
  • NEW callback API: journalBuilder: journal => journal.AddWriteEventAdapter<...>()
  • Explicit JournalOptions + WithJournalAndSnapshot pattern (common in Akka.Persistence.Sql)

This fix is especially critical now that the old Adapters property has been deprecated in #665. All users must now use the callback API which was broken without this fix.

Testing

  • Added comprehensive regression test suite (EventAdapterRuntimeInvocationSpecs.cs)
    • Tests verify adapter runtime invocation with various configuration patterns
    • Covers simple, multi-journal, and explicit JournalOptions scenarios
  • All 20 Akka.Persistence.Hosting.Tests pass ✅
  • Verified backwards compatibility with existing EventAdapterSpecs
  • Tested with Akka.Persistence.Sql - resolves reported issue ✅

Test Evidence

Before Fix

Event adapter was called 0 times
Expected: 3, Actual: 0

After Fix

[TestEventTagger] ToJournal called 1 times. Event: TestEvent, Result: Tagged
[TestEventTagger] ToJournal called 2 times. Event: TestEvent, Result: Tagged
[TestEventTagger] ToJournal called 3 times. Event: TestEvent, Result: Tagged
Test Run Successful.

Related Issues

Changes

  • Modified: src/Akka.Persistence.Hosting/AkkaPersistenceHostingExtensions.cs (removed 1 line)
  • Added: src/Akka.Persistence.Hosting.Tests/EventAdapterRuntimeInvocationSpecs.cs (254 lines, 3 test classes)

Fixed a critical bug where event adapters configured via the NEW callback API
(journalBuilder callback parameter in WithJournal/WithJournalAndSnapshot) were
not being invoked at runtime, despite being correctly present in HOCON configuration.

Root Cause:
AkkaPersistenceJournalBuilder.Build() was creating a Config with
.WithFallback(Persistence.DefaultConfig()) which interfered with the main journal
configuration during HOCON merging, causing adapters to be registered but not invoked.

The Fix:
Removed the unnecessary .WithFallback(Persistence.DefaultConfig()) call in
AkkaPersistenceHostingExtensions.cs line 130. The adapter configuration blocks
(event-adapters and event-adapter-bindings) don't require fallback to default
persistence config and merge correctly with the journal config without it.

Impact:
This bug affected users with:
- Multiple journal configurations (e.g., default + sharding)
- NEW callback API: journalBuilder: journal => journal.AddWriteEventAdapter<...>()
- Explicit JournalOptions + WithJournalAndSnapshot pattern

This fix is especially critical now that the old Adapters property has been
deprecated in akkadotnet#665. All users must use the callback API which was broken.

Testing:
- Added comprehensive regression test suite (EventAdapterRuntimeInvocationSpecs.cs)
- Tests verify adapter runtime invocation with various configuration patterns
- All 20 Akka.Persistence.Hosting.Tests pass
- Verified backwards compatibility with existing EventAdapterSpecs

Related Issues:
- Fixes runtime invocation issue reported in akkadotnet/Akka.Persistence.Sql#552
- Critical fix for akkadotnet#665 callback API requirement
@Aaronontheweb Aaronontheweb force-pushed the fix/event-adapter-runtime-invocation branch from 89c8372 to f7ca2ab Compare October 13, 2025 18:24
@Aaronontheweb
Copy link
Member Author

Updated tests to use AwaitAssertAsync instead of Task.Delay + immediate assertions. This provides proper retry logic and eliminates arbitrary wait times.

Copy link
Member Author

@Aaronontheweb Aaronontheweb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Detailed my changes - I want to improve the tests but the substantial changes are good IMHO

/// This is a regression test for https://github.com/akkadotnet/Akka.Persistence.Sql/issues/552
/// where event adapters were present in HOCON but never invoked at runtime.
/// </summary>
public class EventAdapterRuntimeInvocationSpecs : Akka.Hosting.TestKit.TestKit
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reproduces akkadotnet/Akka.Persistence.Sql#552 using the in-memory journal.


public sealed class InvocationCountingAdapter : IWriteEventAdapter
{
public static int CallCount = 0;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static fields shouldn't / won't work if there are multiple tests all running in the same fixture. Probably should come up with something else here, like an Akka.Persistence.Query replay.

adapters.AppendLine("}");

var finalHocon = ConfigurationFactory.ParseString(adapters.ToString())
.WithFallback(Persistence.DefaultConfig()); // add the default config as a fallback
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the real bug - the default HOCON getting prepended to the front of the configuration causes the event-adapters collection to be empty.

- Remove complex EventsByTag query approach (in-memory journal doesn't support this)
- Use simple static counter with proper resets for test isolation
- Each test explicitly resets the counter at the start
- Maintains same verification: adapters are invoked at runtime
- Keeps all original test scenarios from issue akkadotnet#552 reproduction

Addresses akkadotnet#674 (comment)
- Remove static CallCount field that caused concurrency issues in tests
- Simplify test to use Akka.Persistence.Query with EventsByTag instead of static counters
- Fix hardcoded AkkaVersion "1.5.53" to use $(AkkaVersion) variable in csproj
- Add Akka.Persistence.Query.InMemory package dependency
- Remove overly complex multi-journal test scenarios, focusing on single clear test case

This properly verifies that event adapters configured via the callback API
are invoked at runtime by tagging events and querying for them.
@Aaronontheweb
Copy link
Member Author

Verification Complete ✅

Tested this fix using a local NuGet build (v1.5.52-alpha-1760450556) against the reproduction tests in akkadotnet/Akka.Persistence.Sql#554.

Test Results

NEW Callback API (the affected API from issue #552):

  • RuntimeEventAdapterSpec.EventAdapter_ShouldWork_OnDefaultJournal - PASSES
  • RuntimeEventAdapterSpec.EventAdapter_ShouldWork_OnShardingJournal_ReproducesUserScenario - PASSES

Both tests that use the new callback API pattern now pass with this fix. The sharding journal test specifically reproduces the exact scenario from akkadotnet/Akka.Persistence.Sql#552 where event adapters configured via journalBuilder: parameter were not being invoked.

OLD Deprecated API:

  • RuntimeEventAdapterOldApiSpec.EventAdapter_ShouldWork_WithOldApi - FAILS (Expected)

The old JournalOptions.Adapters property API was intentionally disabled in #669, so this failure is expected and correct.

Conclusion

This fix successfully resolves akkadotnet/Akka.Persistence.Sql#552. Event adapters configured via the new callback API now invoke correctly at runtime in multi-journal configurations.

The root cause fix (removing the .WithFallback(Persistence.DefaultConfig()) call) was exactly right - it was interfering with HOCON merging and preventing adapter registration from taking effect.

Copy link
Contributor

@Arkatufus Arkatufus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Aaronontheweb Aaronontheweb enabled auto-merge (squash) October 14, 2025 14:27
@Aaronontheweb Aaronontheweb merged commit f75c1f4 into akkadotnet:dev Oct 14, 2025
2 checks passed
This was referenced Oct 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants