Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,6 @@ launchSettings.json
# Claude worktree management
.claude-wt/worktrees
/.playwright-mcp

# Test certificate for Akka.Remote.Hosting.Tests (the Akka.Remote.Tests ones are force-added)
!src/contrib/hosting/Akka.Remote.Hosting.Tests/Resources/akka-validcert.pfx
1 change: 1 addition & 0 deletions .incrementalist/testsOnlyNetFx.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"**/.Tests.MultiNode.csproj",
"**/Akka.MultiNode.TestAdapter.Tests.csproj",
"**/Akka.MultiNode.TestAdapter.Xunit2.Tests.csproj",
"src/contrib/hosting/**",
"src/examples/**"
],
"target": [
Expand Down
19 changes: 19 additions & 0 deletions Akka.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,20 @@
<Project Path="src/contrib/dependencyinjection/Akka.DependencyInjection.Tests/Akka.DependencyInjection.Tests.csproj" />
<Project Path="src/contrib/dependencyinjection/Akka.DependencyInjection/Akka.DependencyInjection.csproj" />
</Folder>
<Folder Name="/Contrib/Hosting/">
<Project Path="src/contrib/hosting/Akka.Cluster.Hosting.Tests/Akka.Cluster.Hosting.Tests.csproj" />
<Project Path="src/contrib/hosting/Akka.Cluster.Hosting/Akka.Cluster.Hosting.csproj" />
<Project Path="src/contrib/hosting/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj" />
<Project Path="src/contrib/hosting/Akka.Hosting.TestKit.Xunit2.Tests/Akka.Hosting.TestKit.Xunit2.Tests.csproj" />
<Project Path="src/contrib/hosting/Akka.Hosting.TestKit.Xunit2/Akka.Hosting.TestKit.Xunit2.csproj" />
<Project Path="src/contrib/hosting/Akka.Hosting.TestKit/Akka.Hosting.TestKit.csproj" />
<Project Path="src/contrib/hosting/Akka.Hosting.Tests/Akka.Hosting.Tests.csproj" />
<Project Path="src/contrib/hosting/Akka.Hosting/Akka.Hosting.csproj" />
<Project Path="src/contrib/hosting/Akka.Persistence.Hosting.Tests/Akka.Persistence.Hosting.Tests.csproj" />
<Project Path="src/contrib/hosting/Akka.Persistence.Hosting/Akka.Persistence.Hosting.csproj" />
<Project Path="src/contrib/hosting/Akka.Remote.Hosting.Tests/Akka.Remote.Hosting.Tests.csproj" />
<Project Path="src/contrib/hosting/Akka.Remote.Hosting/Akka.Remote.Hosting.csproj" />
</Folder>
<Folder Name="/Contrib/Persistence/">
<Project Path="src/contrib/persistence/Akka.Persistence.Query.InMemory.Tests/Akka.Persistence.Query.InMemory.Tests.csproj" />
<Project Path="src/contrib/persistence/Akka.Persistence.Query.InMemory/Akka.Persistence.Query.InMemory.csproj" />
Expand Down Expand Up @@ -286,6 +300,11 @@
<Folder Name="/Examples/HelloAkka/">
<Project Path="src/examples/HelloAkka/HelloWorld/HelloWorld.csproj" />
</Folder>
<Folder Name="/Examples/Hosting/">
<Project Path="src/examples/Hosting/Akka.Hosting.Asp.LoggingDemo/Akka.Hosting.Asp.LoggingDemo.csproj" />
<Project Path="src/examples/Hosting/Akka.Hosting.LoggingDemo/Akka.Hosting.LoggingDemo.csproj" />
<Project Path="src/examples/Hosting/Akka.Hosting.SimpleDemo/Akka.Hosting.SimpleDemo.csproj" />
</Folder>
<Folder Name="/Examples/Persistence/">
<Project Path="src/examples/Akka.Persistence.Custom.Tests/Akka.Persistence.Custom.Tests.csproj" />
<Project Path="src/examples/Akka.Persistence.Custom/Akka.Persistence.Custom.csproj" />
Expand Down
1 change: 1 addition & 0 deletions BREAKING_CHANGES_V1.6.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ of `Behavior`, `Wire`, `API` (combine with `+`).

| Status | PR / Branch | Component | Type | Change | Migration |
|--------|-------------|-----------|------|--------|-----------|
| Planned | `feature/hosting-import-dev` (forward-port of [#8591](https://github.com/akkadotnet/akka.net/pull/8591)) | `Akka.Hosting`, `Akka.Remote.Hosting`, `Akka.Cluster.Hosting`, `Akka.Persistence.Hosting`, `Akka.Hosting.TestKit`, `Akka.Hosting.TestKit.Xunit2` | API | The Akka.Hosting packages now ship from this repository at the Akka.NET version. On 1.6 they target `net10.0` only (previously `netstandard2.0;net6.0`), matching the rest of Akka.NET 1.6. `Microsoft.Extensions.*` floor moves to 10.0.0 for `Akka`, `Akka.DependencyInjection` and the Hosting packages. | Reference `Akka.Hosting.*` at the same version as `Akka.*`. Applications must target `net10.0` or later, as for every other Akka.NET 1.6 package. |
| Planned | `fix/testkit-xunit-async-dispose-chain` | `Akka.TestKit.Xunit` | API + Behavior | `Akka.TestKit.Xunit.TestKit` now also implements `Xunit.IAsyncLifetime`, adding `virtual ValueTask InitializeAsync()` and `virtual ValueTask DisposeAsync()`. This closes #8191: xUnit v3 tears a test class instance down via `IAsyncDisposable.DisposeAsync()` in preference to `IDisposable.Dispose()` whenever a type implements both, so a derived spec that declared its own `IAsyncLifetime.DisposeAsync()` (to run its own async teardown) short-circuited `TestKit.Dispose(bool)` entirely and silently leaked its `ActorSystem` -- DotNetty threads, a bound socket, and a `PeriodicTimer`-driven scheduler included. `TestKit.DisposeAsync()` now runs the synchronous dispose chain (`AfterAll()` etc.) and then shuts the system down with the non-blocking `ShutdownAsync()` instead of the blocking `Shutdown()`. | A derived class that already declares its own `InitializeAsync`/`DisposeAsync` (whether via `IAsyncLifetime` directly or by re-declaring the method) now hides a virtual member of the same name and gets `CS0114`, which is an error under this repo's own `-warnaserror` build. Add `override` to each and chain to the base (`await base.InitializeAsync();` / `await base.DisposeAsync();`), and drop the redundant `IAsyncLifetime` interface declaration and any no-op `DisposeAsync` that only returned a completed `ValueTask`. |
| Planned | `fix/8508-clusterclient-reconnect-timer` | `Akka.Cluster.Tools` | API + Behavior | `ClusterClient` now implements `Akka.Actor.IWithTimers` and manages its reconnect, heartbeat and refresh-contacts timers through the keyed timer scheduler. The reconnect deadline is armed once per entry into the establishing phase (`PreStart` and re-establish) instead of on every message handled while establishing, which is the fix for #8508: the old code abandoned one one-shot timer per message and cancelled only the last, so a leftover firing after the client returned to establishing stopped a healthy client mid-reconnect, long before its own deadline was due. API surface: the class gains the `IWithTimers` interface and a public `Timers` property; the `PostStop` override is gone because the timer scheduler cancels timers on stop and restart. `reconnect-timeout = off` (the default) remains a true no-op and arms nothing. Secondary behavior change: because the timers are keyed, a `RefreshContactsTick` still queued from the previous cadence is discarded when the interval changes on a state transition, rather than being delivered against the new cadence. | None. `ClusterClient` is `sealed`, so the added interface and property cannot break a subclass, and the removed `PostStop` override was `protected` on that sealed class and therefore unreachable from outside. Deployments that had enabled `akka.cluster.client.reconnect-timeout` should simply stop seeing clients stopped early during receptionist handover. |
| Planned | [#8324](https://github.com/akkadotnet/akka.net/pull/8324) | `Akka.Routing` | Behavior | `ConsistentHash<T>` no longer retains the `SortedDictionary` passed to its public constructor — the ring is snapshotted into internal sorted arrays. Mutating that dictionary after construction no longer affects the instance (previously the aliasing was inconsistent: `IsEmpty` and `operator +`/`-` read it live, while `NodeFor` froze it after the first lookup). A `null` dictionary now throws `ArgumentNullException` from the constructor instead of surfacing later as a `NullReferenceException`. No public API removed; the ring built by `ConsistentHash.Create` is byte-identical. (#8293) | None for normal use — `ConsistentHash.Create` already builds the dictionary fully before constructing, so routers/receptionists are unaffected. If you call the `ConsistentHash(SortedDictionary, int)` constructor directly, populate the dictionary before passing it and don't rely on post-construction mutation being visible. |
Expand Down
3 changes: 2 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@
<HoconVersion>2.0.3</HoconVersion>
<ConfigurationManagerVersion>6.0.1</ConfigurationManagerVersion>
<MicrosoftLibVersion>[6.0.*,)</MicrosoftLibVersion>
<MsExtVersion>[6.0.*,)</MsExtVersion>
<MsExtVersion>[10.0.0,)</MsExtVersion>
<MsExtTestVersion>10.0.10</MsExtTestVersion>
<OpenTelemetryVersion>[1.15.3,)</OpenTelemetryVersion>
<AkkaAnalyzerVersion>0.3.3</AkkaAnalyzerVersion>
<VerifyXunitV3Version>31.24.2</VerifyXunitV3Version>
<VerifyDiffPlexVersion>3.0.0</VerifyDiffPlexVersion>
Expand Down
2 changes: 2 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

**Placeholder for nightly build**

* Akka.Hosting: Akka.Hosting now ships from this repository. The `Akka.Hosting`, `Akka.Remote.Hosting`, `Akka.Cluster.Hosting`, `Akka.Persistence.Hosting`, `Akka.Hosting.TestKit` and `Akka.Hosting.TestKit.Xunit2` packages live under `src/contrib/hosting` and share the Akka.NET version and release cadence (forward-port of [#8591](https://github.com/akkadotnet/akka.net/pull/8591)). No package IDs, namespaces or public APIs changed; bump `Akka.Hosting.*` to the same version as `Akka.*`. Hosting-only hotfix versions no longer exist. Documentation moved to [getakka.net/articles/hosting](https://getakka.net/articles/hosting/index.html); the [akkadotnet/Akka.Hosting](https://github.com/akkadotnet/Akka.Hosting) repository will be archived and keeps the history for versions up to 1.5.71. **Breaking on 1.6**: the Hosting packages target `net10.0` only, matching the rest of Akka.NET 1.6.
* Build: Microsoft.Extensions minimum version moves to 10.0 for `Akka`, `Akka.DependencyInjection` and all `Akka.Hosting.*` packages (previously 6.0 for the core packages and 9.0 for Akka.Hosting). Akka.Hosting's `OpenTelemetry` dependency moves to 1.15.3 or later, which clears [GHSA-g94r-2vxg-569j](https://github.com/advisories/GHSA-g94r-2vxg-569j).
* Core: Add `ILoggingAdapter` context enrichment, explicit scopes, and bracketed context output in StandardOutLogger and Xunit logger
* Akka.Streams: Add cancellation-aware `Source.Queue` offers so backpressured pending offers can be canceled without later emitting the canceled element.
* Akka.Streams: Fixed `Source.From(IAsyncEnumerable<T>)` cleanup so cancellation waits for any in-flight `MoveNextAsync()` before disposing the async enumerator and its cancellation token source.
Expand Down
4 changes: 2 additions & 2 deletions docs/articles/deployment/akkacluster-troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Generally speaking, unreachable nodes are usually caused by environment problems

#### Use Akka.Hosting

When you use [Akka.Hosting](https://github.com/akkadotnet/Akka.Hosting), this ensures that your `ActorSystem` is managed with the best lifecycle management practices for Akka.NET. Part of this includes making sure that when an Akka.NET process is shutdown it cleanly leaves the cluster first before terminating. One common reason for reachability problems is that during deployments users simply abort the Akka.NET process without letting the `ActorSystem` gracefully terminate, which leaves behind an unreachable node. Akka.Hosting eliminates this problem.
When you use [Akka.Hosting](xref:akka-hosting), this ensures that your `ActorSystem` is managed with the best lifecycle management practices for Akka.NET. Part of this includes making sure that when an Akka.NET process is shutdown it cleanly leaves the cluster first before terminating. One common reason for reachability problems is that during deployments users simply abort the Akka.NET process without letting the `ActorSystem` gracefully terminate, which leaves behind an unreachable node. Akka.Hosting eliminates this problem.

#### Increase Failure Detector Thresholds

Expand All @@ -120,7 +120,7 @@ Please create an issue in our GitHub at [https://github.com/akkadotnet/akka.net]

This typically means that one of the optional serializers built on top of Akka.Cluster is not registered on this node, but this node is still receiving messages from other nodes who are using it. [`DistributedPubSub`](xref:distributed-publish-subscribe) is the most likely culprit when this occurs.

To fix this issue, either use [Akka.Hosting](https://github.com/akkadotnet/Akka.Hosting) or manually register the serializers in your HOCON when you start your `ActorSystem`:
To fix this issue, either use [Akka.Hosting](xref:akka-hosting) or manually register the serializers in your HOCON when you start your `ActorSystem`:

```csharp
Config myHocon = ConfigurationFactory.ParseString("{hocon}");
Expand Down
2 changes: 1 addition & 1 deletion docs/articles/discovery/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Cluster.Bootstrap uses these to dynamically populate the seed nodes list and saf

## Configuration with Akka.Hosting (Recommended)

> **Recommended:** We strongly encourage users to configure Akka.Discovery using [Akka.Hosting](https://github.com/akkadotnet/Akka.Hosting) for a modern, type-safe, and composable experience.
> **Recommended:** We strongly encourage users to configure Akka.Discovery using [Akka.Hosting](xref:akka-hosting) for a modern, type-safe, and composable experience.

Example using Akka.Discovery.Azure with Akka.Hosting, including remoting, clustering, Akka.Management, and Cluster Bootstrap:

Expand Down
87 changes: 87 additions & 0 deletions docs/articles/hosting/configuration-builder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
uid: hosting-configuration-builder
title: The AkkaConfigurationBuilder API
---

# The AkkaConfigurationBuilder API

We want to make Akka.NET something that can be instantiated more typically per the patterns often used with the Microsoft.Extensions.Hosting APIs that are common throughout .NET.

The `AddAkka` extension method on `IServiceCollection` is the entry point into Akka.Hosting - it hands you an `AkkaConfigurationBuilder` that you use to configure and start your `ActorSystem`:

```csharp
using Akka.Hosting;
using Akka.Actor;
using Akka.Actor.Dsl;
using Akka.Cluster.Hosting;
using Akka.Remote.Hosting;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAkka("MyActorSystem", configurationBuilder =>
{
configurationBuilder
.WithRemoting("localhost", 8110)
.WithClustering(new ClusterOptions(){ Roles = new[]{ "myRole" },
SeedNodes = new[]{ Address.Parse("akka.tcp://MyActorSystem@localhost:8110")}})
.WithActors((system, registry) =>
{
var echo = system.ActorOf(act =>
{
act.ReceiveAny((o, context) =>
{
context.Sender.Tell($"{context.Self} rcv {o}");
});
}, "echo");
registry.TryRegister<Echo>(echo); // register for DI
});
});

var app = builder.Build();

app.MapGet("/", async (context) =>
{
var echo = context.RequestServices.GetRequiredService<ActorRegistry>().Get<Echo>();
var body = await echo.Ask<string>(context.TraceIdentifier, context.RequestAborted).ConfigureAwait(false);
await context.Response.WriteAsync(body);
});

app.Run();
```

No HOCON. Automatically runs all Akka.NET application lifecycle best practices behind the scene. Automatically binds the `ActorSystem` and the `ActorRegistry`, another new 1.5 feature, to the `IServiceCollection` so they can be safely consumed via both actors and non-Akka.NET parts of users' .NET applications.

This should be open to extension in other child plugins, such as `Akka.Persistence.SqlServer`:

```csharp
builder.Services.AddAkka("MyActorSystem", configurationBuilder =>
{
configurationBuilder
.WithRemoting("localhost", 8110)
.WithClustering(new ClusterOptions()
{
Roles = new[] { "myRole" },
SeedNodes = new[] { Address.Parse("akka.tcp://MyActorSystem@localhost:8110") }
})
.WithSqlServerPersistence(builder.Configuration.GetConnectionString("sqlServerLocal"))
.WithShardRegion<UserActionsEntity>("userActions", s => UserActionsEntity.Props(s),
new UserMessageExtractor(),
new ShardOptions(){ StateStoreMode = StateStoreMode.DData, Role = "myRole"})
.WithActors((system, registry) =>
{
var userActionsShard = registry.Get<UserActionsEntity>();
var indexer = system.ActorOf(Props.Create(() => new Indexer(userActionsShard)), "index");
registry.TryRegister<Index>(indexer); // register for DI
});
})
```

## Other AkkaConfigurationBuilder Methods

Beyond `WithActors`, the `AkkaConfigurationBuilder` exposes several other methods for advanced configuration scenarios:

* `AddHocon` - merges a HOCON `Config` object, or a `Microsoft.Extensions.Configuration` `IConfiguration` section, into the `ActorSystem` configuration. See [Microsoft.Extensions.Configuration Integration](xref:hosting-configuration) for details.
* `AddSetup` - adds an Akka.NET `Setup` object, such as `BootstrapSetup` or `ServiceProviderSetup`, to the `ActorSystem` startup pipeline for programmatic configuration.
* `WithActorRefProvider` - overrides the `ActorRefProvider` used by the `ActorSystem` (for example, forcing `ActorRefProvider.Local` in a test host that would otherwise default to `ActorRefProvider.Cluster`).

For dependency injection with the `ActorRegistry` and `IRequiredActor<TKey>`, see [Dependency Injection Outside and Inside Akka.NET](xref:hosting-dependency-injection).
Loading
Loading