fix(alarms): truncation-safe transitions; perf: single-pass alarm parse; configurable poll cadence
This commit is contained in:
@@ -46,6 +46,29 @@ public sealed class AlarmsOptions
|
||||
/// </summary>
|
||||
public int ReconcileIntervalSeconds { get; init; } = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Cadence at which the worker's STA polls the AVEVA alarm consumer
|
||||
/// (<c>GetXmlCurrentAlarms2</c>) for the current active-alarm snapshot.
|
||||
/// Default 500 ms; must be at least 100 ms. Every poll is a COM call
|
||||
/// plus an XML parse on the STA that also serves reads and writes, so
|
||||
/// driving it below 100 ms starves the command path. Conveyed to the
|
||||
/// worker through the <c>MXGATEWAY_ALARM_POLL_INTERVAL_MS</c>
|
||||
/// environment variable.
|
||||
/// </summary>
|
||||
public int PollIntervalMilliseconds { get; init; } = 500;
|
||||
|
||||
/// <summary>
|
||||
/// Cap the worker passes to <c>GetXmlCurrentAlarms2</c>'s
|
||||
/// <c>maxAlmCnt</c> argument. Default 1024; must be at least 64. A
|
||||
/// fetch that comes back holding exactly this many records is treated
|
||||
/// as truncated: the worker keeps the alarms the capped fetch could
|
||||
/// not mention in its snapshot rather than letting their absence read
|
||||
/// as a clear. Raise it on galaxies whose steady-state active-alarm
|
||||
/// count approaches the cap. Conveyed to the worker through the
|
||||
/// <c>MXGATEWAY_ALARM_MAX_ALARMS_PER_FETCH</c> environment variable.
|
||||
/// </summary>
|
||||
public int MaxAlarmsPerFetch { get; init; } = 1024;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration for the alarm-manager ↔ subtag fallback mechanism:
|
||||
/// operating mode, failure-detection thresholds, discovery, and subtag
|
||||
|
||||
@@ -414,8 +414,26 @@ public sealed class GatewayOptionsValidator : OptionsValidatorBase<GatewayOption
|
||||
|
||||
private static readonly string[] ValidAlarmFallbackModes = ["Auto", "ForceAlarmManager", "ForceSubtag"];
|
||||
|
||||
private const int MinimumAlarmPollIntervalMilliseconds = 100;
|
||||
private const int MinimumMaxAlarmsPerFetch = 64;
|
||||
|
||||
private static void ValidateAlarms(AlarmsOptions options, ValidationBuilder builder)
|
||||
{
|
||||
// Validated regardless of Enabled: both values are stamped onto every
|
||||
// worker launch environment, so a bad value is a misconfiguration even
|
||||
// before the central monitor is switched on.
|
||||
if (options.PollIntervalMilliseconds < MinimumAlarmPollIntervalMilliseconds)
|
||||
{
|
||||
builder.Add(
|
||||
$"MxGateway:Alarms:PollIntervalMilliseconds must be greater than or equal to {MinimumAlarmPollIntervalMilliseconds}.");
|
||||
}
|
||||
|
||||
if (options.MaxAlarmsPerFetch < MinimumMaxAlarmsPerFetch)
|
||||
{
|
||||
builder.Add(
|
||||
$"MxGateway:Alarms:MaxAlarmsPerFetch must be greater than or equal to {MinimumMaxAlarmsPerFetch}.");
|
||||
}
|
||||
|
||||
if (!options.Enabled)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -29,11 +29,27 @@ public sealed class WorkerProcessLauncher : IWorkerProcessLauncher
|
||||
public const string WorkerWriteCompletionWaitEnvironmentVariableName =
|
||||
"MXGATEWAY_WORKER_WRITE_COMPLETION_WAIT_MS";
|
||||
|
||||
/// <summary>
|
||||
/// Conveys MxGateway:Alarms:PollIntervalMilliseconds to the worker: the
|
||||
/// cadence at which the worker's STA polls the AVEVA alarm consumer.
|
||||
/// </summary>
|
||||
public const string WorkerAlarmPollIntervalEnvironmentVariableName =
|
||||
"MXGATEWAY_ALARM_POLL_INTERVAL_MS";
|
||||
|
||||
/// <summary>
|
||||
/// Conveys MxGateway:Alarms:MaxAlarmsPerFetch to the worker: the cap
|
||||
/// passed to GetXmlCurrentAlarms2, which is also the record count at
|
||||
/// which the worker treats a fetch as truncated.
|
||||
/// </summary>
|
||||
public const string WorkerMaxAlarmsPerFetchEnvironmentVariableName =
|
||||
"MXGATEWAY_ALARM_MAX_ALARMS_PER_FETCH";
|
||||
|
||||
private readonly IWorkerProcessFactory _processFactory;
|
||||
private readonly IWorkerStartupProbe _startupProbe;
|
||||
private readonly GatewayMetrics _metrics;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly WorkerOptions _workerOptions;
|
||||
private readonly AlarmsOptions _alarmsOptions;
|
||||
private readonly ILogger<WorkerProcessLauncher> _logger;
|
||||
|
||||
/// <summary>
|
||||
@@ -59,6 +75,7 @@ public sealed class WorkerProcessLauncher : IWorkerProcessLauncher
|
||||
ArgumentNullException.ThrowIfNull(metrics);
|
||||
|
||||
_workerOptions = gatewayOptions.Value.Worker;
|
||||
_alarmsOptions = gatewayOptions.Value.Alarms;
|
||||
_processFactory = processFactory;
|
||||
_startupProbe = startupProbe;
|
||||
_metrics = metrics;
|
||||
@@ -185,6 +202,10 @@ public sealed class WorkerProcessLauncher : IWorkerProcessLauncher
|
||||
_workerOptions.PipeConnectAttemptTimeoutMilliseconds.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
startInfo.Environment[WorkerWriteCompletionWaitEnvironmentVariableName] =
|
||||
_workerOptions.WriteCompletionWaitMilliseconds.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
startInfo.Environment[WorkerAlarmPollIntervalEnvironmentVariableName] =
|
||||
_alarmsOptions.PollIntervalMilliseconds.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
startInfo.Environment[WorkerMaxAlarmsPerFetchEnvironmentVariableName] =
|
||||
_alarmsOptions.MaxAlarmsPerFetch.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
commandLine = new WorkerProcessCommandLine(executablePath, arguments);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user