refactor(host)/test: M2.14 review nits — simplify probe cancellation + pre-cancelled-token test (#28)
- Remove redundant linked CancellationTokenSource in ProbeAsync; pass the framework cancellationToken and ProbeTimeout directly to Ask (the two-CTS pattern was redundant — Ask already honours both the timeout and the token). - Add EchoActor XML <remarks> explaining why no Receive<Identify> handler is needed (ActorBase answers Identify automatically). - Add PreCancelledToken_ReportsUnhealthy_DoesNotThrow test: verifies the never-throws guarantee on the shutdown-race path (token already cancelled before CheckHealthAsync is invoked).
This commit is contained in:
@@ -157,11 +157,9 @@ public sealed class RequiredSingletonsHealthCheck : IHealthCheck
|
|||||||
// ActorSelection so a missing path resolves an ActorIdentity with a null
|
// ActorSelection so a missing path resolves an ActorIdentity with a null
|
||||||
// Subject (rather than throwing) within the bounded timeout.
|
// Subject (rather than throwing) within the bounded timeout.
|
||||||
var selection = system.ActorSelection($"/user/{proxyName}");
|
var selection = system.ActorSelection($"/user/{proxyName}");
|
||||||
using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
|
||||||
cts.CancelAfter(ProbeTimeout);
|
|
||||||
|
|
||||||
var identity = await selection
|
var identity = await selection
|
||||||
.Ask<ActorIdentity>(new Identify(proxyName), ProbeTimeout, cts.Token)
|
.Ask<ActorIdentity>(new Identify(proxyName), ProbeTimeout, cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
return (proxyName, identity.Subject is not null);
|
return (proxyName, identity.Subject is not null);
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ public class RequiredSingletonsHealthCheckTests : TestKit
|
|||||||
{
|
{
|
||||||
/// <summary>A minimal live actor that does nothing — its mere existence makes
|
/// <summary>A minimal live actor that does nothing — its mere existence makes
|
||||||
/// an <see cref="Identify"/> resolve a non-null Subject (i.e. "reachable").</summary>
|
/// an <see cref="Identify"/> resolve a non-null Subject (i.e. "reachable").</summary>
|
||||||
|
/// <remarks>No <c>Receive<Identify></c> handler is needed: Akka's
|
||||||
|
/// <see cref="ActorBase"/> answers every <see cref="Identify"/> message with
|
||||||
|
/// an <see cref="ActorIdentity"/> automatically, so an empty actor at the proxy
|
||||||
|
/// path is sufficient to simulate a reachable singleton.</remarks>
|
||||||
private sealed class EchoActor : ReceiveActor
|
private sealed class EchoActor : ReceiveActor
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -106,4 +110,34 @@ public class RequiredSingletonsHealthCheckTests : TestKit
|
|||||||
|
|
||||||
Assert.Equal(HealthStatus.Unhealthy, result.Status);
|
Assert.Equal(HealthStatus.Unhealthy, result.Status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PreCancelledToken_ReportsUnhealthy_DoesNotThrow()
|
||||||
|
{
|
||||||
|
// Shutdown-race path: CheckHealthAsync is called with an already-cancelled
|
||||||
|
// token (e.g. host is tearing down). The check must never throw — any
|
||||||
|
// OperationCanceledException from Ask must be caught and mapped to Unhealthy.
|
||||||
|
foreach (var name in RequiredSingletonsHealthCheck.RequiredSingletonProxyNames)
|
||||||
|
{
|
||||||
|
Sys.ActorOf(Props.Create(() => new EchoActor()), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
var check = new RequiredSingletonsHealthCheck(
|
||||||
|
ProviderReturning(Sys),
|
||||||
|
NullLogger<RequiredSingletonsHealthCheck>.Instance);
|
||||||
|
|
||||||
|
using var cts = new CancellationTokenSource();
|
||||||
|
cts.Cancel(); // already cancelled before the check runs
|
||||||
|
|
||||||
|
var context = new HealthCheckContext
|
||||||
|
{
|
||||||
|
Registration = new HealthCheckRegistration(
|
||||||
|
"required-singletons", check, failureStatus: null, tags: null),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Must not throw; an already-cancelled token → all probes fail → Unhealthy.
|
||||||
|
var result = await check.CheckHealthAsync(context, cts.Token);
|
||||||
|
|
||||||
|
Assert.Equal(HealthStatus.Unhealthy, result.Status);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user