Skip to content

Revert Task.Yield() from AsyncWriteJournal and SnapshotStore (cherry-pick to dev)#8189

Merged
Aaronontheweb merged 2 commits into
akkadotnet:devfrom
Aaronontheweb:fix/revert-task-yield-8163-dev
Apr 26, 2026
Merged

Revert Task.Yield() from AsyncWriteJournal and SnapshotStore (cherry-pick to dev)#8189
Aaronontheweb merged 2 commits into
akkadotnet:devfrom
Aaronontheweb:fix/revert-task-yield-8163-dev

Conversation

@Aaronontheweb

Copy link
Copy Markdown
Member

Summary

Cherry-pick of the v1.5.67 hotfix to dev.

See #8188 and the v1.5.67 release notes for full context on why this was reverted.

Test plan

  • Verified on v1.5 branch: Akka.Persistence.Tests — 285 passed, 0 failed
  • Verified on v1.5 branch: Akka.Persistence.TestKit.Tests — 37 passed, 0 failed
  • CI on dev branch

…napshotStore (akkadotnet#8163)

This reverts the Task.Yield() additions from PR akkadotnet#8163 in AsyncWriteJournal.ExecuteBatch
and SnapshotStore.ReceiveSnapshotStore, while preserving the health check test
improvements from that same PR.

PR akkadotnet#8163 added `await Task.Yield()` before calling `WriteMessagesAsync` and `SaveAsync`
inside their respective circuit breaker lambdas. The intent was to move expensive byte
serialization off the actor's message-processing thread, which showed ~45% throughput
improvement in benchmarks.

However, this silently broke the implicit contract that persistence plugins relied on:
that the synchronous preamble of `WriteMessagesAsync`/`SaveAsync` executes in actor
context. Moving execution to the thread pool caused:

1. Plugins that access `Self` inside `WriteMessagesAsync` (e.g. Akka.Persistence.Sql,
   Akka.Persistence.EventStore) throw `NotSupportedException` because there is no
   active ActorContext on a thread pool thread.

2. Plugins that use non-thread-safe collections like `Dictionary<string, Task>` for
   write tracking (e.g. Akka.Persistence.Sql, Akka.Persistence.EventStore) are now
   subject to concurrent access from both the actor thread and thread pool threads,
   causing `InvalidOperationException` or silent data corruption.

3. Plugins that send messages to subscribers after writes complete (e.g.
   Akka.Persistence.Redis) access shared actor state off the actor thread.

The change was too blunt an instrument — it applied uniformly to all plugins via the
base class, removing their ability to do any actor-thread setup before async work begins.
Ironically, the plugins that benefit most from off-thread serialization (MongoDB, Azure
Table Storage) don't access actor context at all, while the plugins that break (SQL,
EventStore, Redis) already perform serialization off-thread in their async pipelines.

A future version may reintroduce this optimization with a more surgical approach
(e.g. opt-in property or Template Method pattern) that preserves the plugin threading
contract.
@Aaronontheweb Aaronontheweb enabled auto-merge (rebase) April 26, 2026 00:42
@Aaronontheweb Aaronontheweb disabled auto-merge April 26, 2026 00:42
@Aaronontheweb Aaronontheweb enabled auto-merge (squash) April 26, 2026 00:42
@Aaronontheweb Aaronontheweb merged commit e7366a0 into akkadotnet:dev Apr 26, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant