feat(runtime): ServerHistorian HistoryRead parallelism/limiter/deadline knobs + node-manager wiring (03/S3)

This commit is contained in:
Joseph Doherty
2026-07-13 12:03:27 -04:00
parent d5c6609cdf
commit 304442dba4
5 changed files with 117 additions and 1 deletions
@@ -68,6 +68,32 @@ public sealed class ServerHistorianOptions
/// </summary>
public int MaxTieClusterOverfetch { get; init; } = 65536;
/// <summary>
/// Maximum number of historized nodes served CONCURRENTLY within one HistoryRead batch (arch-review
/// 03/S3). The SDK's <c>HistoryRead*</c> override surface is synchronous, so each arm block-bridges
/// once at its boundary; this bound turns the per-node fan-out inside that boundary from sequential
/// (worst case <c>N × CallTimeout</c>) into <c>⌈N / P⌉ × CallTimeout</c>. It also caps the gateway-load
/// multiplication a single client can command. Default 4.
/// </summary>
public int HistoryReadBatchParallelism { get; init; } = 4;
/// <summary>
/// Process-wide limit on the number of HistoryRead batches served CONCURRENTLY (arch-review 03/S3).
/// A flood of history clients degrades to <c>BadTooManyOperations</c> rather than exhausting the SDK
/// request-thread pool. Together with <see cref="HistoryReadBatchParallelism"/> this caps total
/// in-flight gateway reads at <c>MaxConcurrentHistoryReadBatches × HistoryReadBatchParallelism</c>
/// (default 16 × 4 = 64). Default 16.
/// </summary>
public int MaxConcurrentHistoryReadBatches { get; init; } = 16;
/// <summary>
/// Per-request wall-clock deadline for a HistoryRead batch (arch-review 03/S3). A single request can
/// no longer hold an SDK request thread longer than this regardless of node count; a node still
/// in flight at the deadline surfaces <c>BadTimeout</c>. Non-positive = unbounded (a
/// <see cref="Validate"/> warning). Default 60 seconds.
/// </summary>
public TimeSpan HistoryReadDeadline { get; init; } = TimeSpan.FromSeconds(60);
/// <summary>Returns operator-facing misconfiguration warnings for an <c>Enabled</c> historian
/// (empty when disabled or correctly configured). Pure — the registration logs each entry.
/// <para>
@@ -100,6 +126,14 @@ public sealed class ServerHistorianOptions
// so a zero/negative value is harmless (and noise-free) when the historian is disabled.
if (MaxTieClusterOverfetch <= 0)
warnings.Add($"ServerHistorian:MaxTieClusterOverfetch is {MaxTieClusterOverfetch} — must be > 0; HistoryRead-Raw cannot page within an oversized tie cluster and will surface BadHistoryOperationUnsupported for those reads.");
// S3 HistoryRead concurrency knobs — a non-positive value disables the corresponding bound, so warn
// (these gate the per-node fan-out / batch limiter that only run when a real data source is wired).
if (HistoryReadBatchParallelism <= 0)
warnings.Add($"ServerHistorian:HistoryReadBatchParallelism is {HistoryReadBatchParallelism} — must be > 0; HistoryRead falls back to serving each node sequentially.");
if (MaxConcurrentHistoryReadBatches <= 0)
warnings.Add($"ServerHistorian:MaxConcurrentHistoryReadBatches is {MaxConcurrentHistoryReadBatches} — must be > 0; the process-wide HistoryRead batch limiter is disabled.");
if (HistoryReadDeadline <= TimeSpan.Zero)
warnings.Add($"ServerHistorian:HistoryReadDeadline is {HistoryReadDeadline} — must be > 0; HistoryRead requests run unbounded (no per-request deadline).");
return warnings;
}
}