fix(site-runtime): resolve SiteRuntime-012,013,015,016 — doc accuracy, shared LoggerFactory, execution-actor coverage; SiteRuntime-014 deferred
This commit is contained in:
@@ -34,6 +34,14 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers
|
||||
private readonly SiteStreamManager? _streamManager;
|
||||
private readonly SiteRuntimeOptions _options;
|
||||
private readonly ILogger<DeploymentManagerActor> _logger;
|
||||
/// <summary>
|
||||
/// Shared logger factory used to mint <see cref="InstanceActor"/> loggers
|
||||
/// (SiteRuntime-015). Reused across every <see cref="CreateInstanceActor"/>
|
||||
/// call rather than newing a per-instance factory that is never disposed.
|
||||
/// When the host injects its configured factory the Instance Actor logs are
|
||||
/// routed through the application's logging providers.
|
||||
/// </summary>
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly IActorRef? _dclManager;
|
||||
private readonly IActorRef? _replicationActor;
|
||||
private readonly ISiteHealthCollector? _healthCollector;
|
||||
@@ -59,7 +67,8 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers
|
||||
IActorRef? dclManager = null,
|
||||
IActorRef? replicationActor = null,
|
||||
ISiteHealthCollector? healthCollector = null,
|
||||
IServiceProvider? serviceProvider = null)
|
||||
IServiceProvider? serviceProvider = null,
|
||||
ILoggerFactory? loggerFactory = null)
|
||||
{
|
||||
_storage = storage;
|
||||
_compilationService = compilationService;
|
||||
@@ -71,6 +80,13 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers
|
||||
_healthCollector = healthCollector;
|
||||
_serviceProvider = serviceProvider;
|
||||
_logger = logger;
|
||||
// SiteRuntime-015: reuse a single logger factory for all Instance Actors.
|
||||
// Prefer an explicitly injected factory, fall back to one resolved from
|
||||
// the service provider, and only as a last resort use NullLoggerFactory —
|
||||
// never a per-instance `new LoggerFactory()` that leaks undisposed.
|
||||
_loggerFactory = loggerFactory
|
||||
?? serviceProvider?.GetService(typeof(ILoggerFactory)) as ILoggerFactory
|
||||
?? Microsoft.Extensions.Logging.Abstractions.NullLoggerFactory.Instance;
|
||||
|
||||
// Lifecycle commands
|
||||
Receive<DeployInstanceCommand>(HandleDeploy);
|
||||
@@ -942,7 +958,8 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers
|
||||
return;
|
||||
}
|
||||
|
||||
var loggerFactory = new LoggerFactory();
|
||||
// SiteRuntime-015: reuse the shared, host-configured logger factory
|
||||
// instead of allocating (and leaking) a fresh LoggerFactory per instance.
|
||||
var props = Props.Create(() => new InstanceActor(
|
||||
instanceName,
|
||||
configJson,
|
||||
@@ -951,7 +968,7 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers
|
||||
_sharedScriptLibrary,
|
||||
_streamManager,
|
||||
_options,
|
||||
loggerFactory.CreateLogger<InstanceActor>(),
|
||||
_loggerFactory.CreateLogger<InstanceActor>(),
|
||||
_dclManager,
|
||||
_healthCollector,
|
||||
_serviceProvider));
|
||||
|
||||
@@ -494,12 +494,19 @@ public class InstanceActor : ReceiveActor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WP-25: Debug view unsubscribe — removes subscription.
|
||||
/// WP-25: Debug view unsubscribe (SiteRuntime-013).
|
||||
/// This handler is a deliberate no-op acknowledgement: the Instance Actor holds
|
||||
/// no per-subscriber state. The real debug-stream subscription lifecycle lives in
|
||||
/// <see cref="ScadaLink.SiteRuntime.Streaming.SiteStreamManager"/>
|
||||
/// (Subscribe / Unsubscribe / RemoveSubscriber); the gRPC stream is torn down
|
||||
/// there when the central side cancels the call. Nothing is removed here.
|
||||
/// </summary>
|
||||
private void HandleUnsubscribeDebugView(UnsubscribeDebugViewRequest request)
|
||||
{
|
||||
// No subscription state in the Instance Actor — see the XML doc above.
|
||||
_logger.LogDebug(
|
||||
"Debug view unsubscribe for {Instance}, correlationId={Id}",
|
||||
"Debug view unsubscribe for {Instance}, correlationId={Id} " +
|
||||
"(no-op; subscription teardown handled by SiteStreamManager)",
|
||||
_instanceUniqueName, request.CorrelationId);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,18 @@ namespace ScadaLink.SiteRuntime.Scripts;
|
||||
/// Scope-aware view onto the instance's attributes, anchored at a path prefix.
|
||||
/// <c>Attributes["X"]</c> on the root scope resolves to canonical name "X";
|
||||
/// on a composition with prefix "TempSensor" it resolves to "TempSensor.X".
|
||||
/// Reads block on the actor Ask; async variants are provided for callers
|
||||
/// that prefer to await explicitly.
|
||||
///
|
||||
/// <para>
|
||||
/// Thread-model note (SiteRuntime-012): the indexer get/set block synchronously
|
||||
/// on the Instance Actor Ask (and, for data-connected attributes, the DCL
|
||||
/// round-trip). This is safe because script bodies execute on the dedicated
|
||||
/// <see cref="ScriptExecutionScheduler"/> threads (SiteRuntime-009), not the
|
||||
/// shared <see cref="System.Threading.ThreadPool"/> — so a blocked accessor
|
||||
/// cannot starve unrelated Akka dispatchers or HTTP request handling. The async
|
||||
/// variants (<see cref="GetAsync"/>/<see cref="SetAsync"/>) are still preferred
|
||||
/// where the script can await, as they avoid holding a dedicated thread idle for
|
||||
/// the duration of each round-trip.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public class AttributeAccessor
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user