8a9bf23048
Review R1 M1: StopChild evicted the hub cache, but DriverResilienceStatusPublisherService re-emits the tracker every tick. IDriverCapabilityInvokerFactory.Release (default no-op) now invalidates the pipeline builder + status tracker; the host calls it before Evict. Test runs a publisher tick after removal, with a neighbour instance as positive control. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
58 lines
3.5 KiB
C#
58 lines
3.5 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
|
|
/// <summary>
|
|
/// Builds a per-driver-instance <see cref="IDriverCapabilityInvoker"/>. The Runtime
|
|
/// <c>DriverHostActor</c> consumes this factory (resolved from DI, like <see cref="IDriverFactory"/>)
|
|
/// to attach a resilience invoker to each spawned <c>DriverInstanceActor</c> WITHOUT the actor
|
|
/// assembly referencing <c>ZB.MOM.WW.OtOpcUa.Core</c> (which drags in Polly). The fused Host
|
|
/// binds a concrete factory closing over the process-singleton pipeline builder + status tracker +
|
|
/// driver-tier resolver; when unbound, <see cref="NullDriverCapabilityInvokerFactory"/> yields the
|
|
/// pass-through invoker so dispatch behaves exactly as before.
|
|
/// </summary>
|
|
public interface IDriverCapabilityInvokerFactory
|
|
{
|
|
/// <summary>
|
|
/// Create the resilience invoker for one driver instance. The returned invoker is keyed on
|
|
/// <paramref name="driverInstanceId"/> and applies the tier policy resolved from
|
|
/// <paramref name="driverType"/>, layering any per-instance
|
|
/// <paramref name="resilienceConfigJson"/> overrides on top.
|
|
/// </summary>
|
|
/// <param name="driverInstanceId">The driver instance identifier (pipeline + tracker key).</param>
|
|
/// <param name="driverType">The driver type name, used to resolve the resilience tier defaults.</param>
|
|
/// <param name="resilienceConfigJson">
|
|
/// Optional per-instance <c>DriverInstance.ResilienceConfig</c> JSON (from the deployment artifact)
|
|
/// layered on top of the tier defaults. <c>null</c>/empty ⇒ pure tier defaults. A concrete factory
|
|
/// invalidates any cached pipelines for <paramref name="driverInstanceId"/> so a changed config
|
|
/// applied across a driver respawn takes effect (the pipeline cache is keyed on instance/host/
|
|
/// capability, not options).
|
|
/// </param>
|
|
/// <returns>A per-instance <see cref="IDriverCapabilityInvoker"/>.</returns>
|
|
IDriverCapabilityInvoker Create(string driverInstanceId, string driverType, string? resilienceConfigJson = null);
|
|
|
|
/// <summary>
|
|
/// Release every piece of shared resilience state held for <paramref name="driverInstanceId"/> — called
|
|
/// by the host when the instance leaves the deployed plan (#609). A concrete factory drops the
|
|
/// instance's cached pipelines and its status-tracker snapshots, so the periodic resilience publisher
|
|
/// stops re-emitting (and re-priming new telemetry subscribers with) a decommissioned driver's last
|
|
/// breaker/failure counters. Default: no-op (a factory that holds no state).
|
|
/// </summary>
|
|
/// <param name="driverInstanceId">The driver instance that was removed.</param>
|
|
void Release(string driverInstanceId) { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns <see cref="NullDriverCapabilityInvoker.Instance"/> for every driver instance. Bound
|
|
/// when the fused Host hasn't registered a real resilience factory (Mac dev path, smoke tests,
|
|
/// admin-only nodes). Dispatch then routes calls straight through with no pipeline.
|
|
/// </summary>
|
|
public sealed class NullDriverCapabilityInvokerFactory : IDriverCapabilityInvokerFactory
|
|
{
|
|
/// <summary>The shared singleton instance.</summary>
|
|
public static readonly NullDriverCapabilityInvokerFactory Instance = new();
|
|
private NullDriverCapabilityInvokerFactory() { }
|
|
|
|
/// <inheritdoc />
|
|
public IDriverCapabilityInvoker Create(string driverInstanceId, string driverType, string? resilienceConfigJson = null) =>
|
|
NullDriverCapabilityInvoker.Instance;
|
|
}
|