refactor(#655): delete the dead driver-side history capability
HistoryRead is served server-side by IHistorianDataSource; nothing ever dispatched to a driver's IHistoryProvider. Removes IHistoryProvider, the OpcUaClient history passthrough, DriverCapability.HistoryRead (+ resilience tier rows and AdminUI row), HistoryReadStatusException and its node-manager catches, and the tests that only covered them. Shared result types and HistoryAggregateNotSupportedException stay (moved to Historian/HistoryReadContracts.cs). A stored ResilienceConfig still carrying a HistoryRead key parses (diagnostic only; new test). Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -5,10 +5,10 @@ namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
/// Each value corresponds to one method (or tightly-related method group) on the
|
||||
/// <c>Core.Abstractions</c> capability interfaces (<see cref="IReadable"/>, <see cref="IWritable"/>,
|
||||
/// <see cref="ITagDiscovery"/>, <see cref="ISubscribable"/>, <see cref="IHostConnectivityProbe"/>,
|
||||
/// <see cref="IAlarmSource"/>, <see cref="IHistoryProvider"/>).
|
||||
/// <see cref="IAlarmSource"/>).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Per the per-capability retry policy in <c>docs/v2/plan.md</c>: Read / HistoryRead /
|
||||
/// Per the per-capability retry policy in <c>docs/v2/plan.md</c>: Read /
|
||||
/// Discover / Probe / AlarmSubscribe auto-retry; <see cref="Write"/> does NOT retry unless the
|
||||
/// tag-definition carries <see cref="WriteIdempotentAttribute"/>. Alarm-acknowledge is treated
|
||||
/// as a write for retry semantics (an alarm-ack is not idempotent at the plant-floor acknowledgement
|
||||
@@ -37,9 +37,6 @@ public enum DriverCapability
|
||||
/// <summary><see cref="IAlarmSource.AcknowledgeAsync"/>. Does NOT retry — ack is a write-shaped operation.</summary>
|
||||
AlarmAcknowledge,
|
||||
|
||||
/// <summary><see cref="IHistoryProvider"/> reads (Raw/Processed/AtTime/Events). Retries by default.</summary>
|
||||
HistoryRead,
|
||||
|
||||
/// <summary>
|
||||
/// <c>IDriver.InitializeAsync</c> — connection establishment (R2-10 T10 closure: without this,
|
||||
/// a driver that cannot connect contributes zero breaker input and the resilience panel stays
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
|
||||
// Shared history result contracts used by IHistorianDataSource and the historian gateway.
|
||||
// Drivers carry no history capability (Gitea #655): HistoryRead is served server-side.
|
||||
|
||||
|
||||
/// <summary>Result of a HistoryRead call.</summary>
|
||||
/// <param name="Samples">Returned samples in chronological order.</param>
|
||||
/// <param name="ContinuationPoint">Opaque token for the next call when more samples are available; null when complete.</param>
|
||||
public sealed record HistoryReadResult(
|
||||
IReadOnlyList<DataValueSnapshot> Samples,
|
||||
byte[]? ContinuationPoint);
|
||||
|
||||
/// <summary>Aggregate function for processed history reads. Mirrors OPC UA Part 13 standard aggregates.</summary>
|
||||
/// <remarks>
|
||||
/// The first five members are the original set every historian backend serves. The remainder (#285) are
|
||||
/// the further standard Part 13 aggregates a server-side HistoryRead can forward to a backend that
|
||||
/// supports them (a gateway backend maps them to its own aggregate modes). A backend that cannot serve a
|
||||
/// member must throw <see cref="HistoryAggregateNotSupportedException"/> — the server maps that to
|
||||
/// <c>BadAggregateNotSupported</c> rather than a generic failure. <b>Append only</b>: the numeric values
|
||||
/// are not persisted, but every <c>switch</c> over this enum must keep a total default.
|
||||
/// </remarks>
|
||||
public enum HistoryAggregateType
|
||||
{
|
||||
Average,
|
||||
Minimum,
|
||||
Maximum,
|
||||
Total,
|
||||
Count,
|
||||
Interpolative,
|
||||
TimeAverage,
|
||||
Start,
|
||||
End,
|
||||
Delta,
|
||||
Range,
|
||||
MinimumActualTime,
|
||||
MaximumActualTime,
|
||||
StartBound,
|
||||
EndBound,
|
||||
DeltaBounds,
|
||||
DurationGood,
|
||||
DurationBad,
|
||||
PercentGood,
|
||||
PercentBad,
|
||||
WorstQuality,
|
||||
NumberOfTransitions,
|
||||
AnnotationCount,
|
||||
StandardDeviationPopulation,
|
||||
VariancePopulation,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thrown by a history backend when it cannot serve the requested <see cref="HistoryAggregateType"/>.
|
||||
/// The Server HistoryRead path translates it to <c>BadAggregateNotSupported</c> for that node only.
|
||||
/// </summary>
|
||||
public sealed class HistoryAggregateNotSupportedException : NotSupportedException
|
||||
{
|
||||
/// <summary>The aggregate the backend could not serve.</summary>
|
||||
public HistoryAggregateType Aggregate { get; }
|
||||
|
||||
/// <summary>Creates the exception for <paramref name="aggregate"/>.</summary>
|
||||
/// <param name="aggregate">The unsupported aggregate.</param>
|
||||
public HistoryAggregateNotSupportedException(HistoryAggregateType aggregate)
|
||||
: base($"The historian backend does not support the {aggregate} aggregate.")
|
||||
=> Aggregate = aggregate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// One row returned by <see cref="IHistorianDataSource.ReadEventsAsync"/> — a historical
|
||||
/// alarm/event record, not the OPC UA live-event stream. Fields match the minimum set the
|
||||
/// Server needs to populate a <c>HistoryEventFieldList</c> for HistoryReadEvents responses.
|
||||
/// </summary>
|
||||
/// <param name="EventId">Stable unique id for the event — driver-specific format.</param>
|
||||
/// <param name="SourceName">Source object that emitted the event. May differ from the <c>sourceName</c> filter the caller passed (fuzzy matches).</param>
|
||||
/// <param name="EventTimeUtc">Process-side timestamp — when the event actually occurred.</param>
|
||||
/// <param name="ReceivedTimeUtc">Historian-side timestamp — when the historian persisted the row; may lag <paramref name="EventTimeUtc"/> by the historian's buffer flush cadence.</param>
|
||||
/// <param name="Message">Human-readable message text.</param>
|
||||
/// <param name="Severity">OPC UA severity (1-1000). Backends map their native priority scale onto this range.</param>
|
||||
public sealed record HistoricalEvent(
|
||||
string EventId,
|
||||
string? SourceName,
|
||||
DateTime EventTimeUtc,
|
||||
DateTime ReceivedTimeUtc,
|
||||
string? Message,
|
||||
ushort Severity);
|
||||
|
||||
/// <summary>Result of a <see cref="IHistorianDataSource.ReadEventsAsync"/> call.</summary>
|
||||
/// <param name="Events">Events in chronological order by <c>EventTimeUtc</c>.</param>
|
||||
/// <param name="ContinuationPoint">Opaque token for the next call when more events are available; null when complete.</param>
|
||||
public sealed record HistoricalEventsResult(
|
||||
IReadOnlyList<HistoricalEvent> Events,
|
||||
byte[]? ContinuationPoint);
|
||||
@@ -5,15 +5,9 @@ namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
/// and resolved per OPC UA namespace, independent of any driver's lifecycle.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Distinct from <see cref="IHistoryProvider"/>:
|
||||
/// <list type="bullet">
|
||||
/// <item><see cref="IHistoryProvider"/> is a *driver capability* — the server
|
||||
/// dispatches to it via the driver instance.</item>
|
||||
/// <item><see cref="IHistorianDataSource"/> is a *server registration* — the
|
||||
/// server resolves it via namespace and calls it directly, so a single
|
||||
/// historian (e.g. Wonderware) can serve many drivers' nodes, and drivers can
|
||||
/// restart without dropping history availability.</item>
|
||||
/// </list>
|
||||
/// This is a *server registration*, not a driver capability: drivers carry no history
|
||||
/// surface (Gitea #655). The server calls it directly, so a single historian can serve
|
||||
/// many drivers' nodes, and drivers can restart without dropping history availability.
|
||||
/// All values returned use the shared <see cref="DataValueSnapshot"/> /
|
||||
/// <see cref="HistoricalEvent"/> shapes; backend-specific quality / type encodings
|
||||
/// are translated to OPC UA <c>StatusCode</c> uints inside the data source.
|
||||
@@ -86,9 +80,7 @@ public interface IHistorianDataSource : IDisposable
|
||||
/// equivalent "use default" sentinel.
|
||||
///
|
||||
/// This surface declares <see cref="ReadAtTimeAsync"/> and <see cref="ReadEventsAsync"/>
|
||||
/// as required members — a server-side historian owns the full read surface, unlike
|
||||
/// <see cref="IHistoryProvider"/> where the same two methods are optional default-impl
|
||||
/// methods so legacy drivers can stay raw-only. The asymmetry is intentional.
|
||||
/// as required members — a server-side historian owns the full read surface.
|
||||
/// </remarks>
|
||||
/// <param name="sourceName">The source name to filter events, or null to return events from all sources.</param>
|
||||
/// <param name="startUtc">The start of the time range in UTC.</param>
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
/// Required capability for every driver instance. Owns lifecycle, metadata, health.
|
||||
/// Other capabilities (<see cref="ITagDiscovery"/>, <see cref="IReadable"/>,
|
||||
/// <see cref="IWritable"/>, <see cref="ISubscribable"/>, <see cref="IAlarmSource"/>,
|
||||
/// <see cref="IHistoryProvider"/>, <see cref="IRediscoverable"/>,
|
||||
/// <see cref="IRediscoverable"/>,
|
||||
/// <see cref="IHostConnectivityProbe"/>) are composable — a driver implements only what its
|
||||
/// backend actually supports.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,237 +0,0 @@
|
||||
namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Driver capability for historical-data reads (OPC UA HistoryRead). Optional —
|
||||
/// only drivers whose backends carry historian data implement this. Currently:
|
||||
/// Galaxy (Wonderware Historian via the optional plugin), OPC UA Client (forward
|
||||
/// to upstream server).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="ReadAtTimeAsync"/> and <see cref="ReadEventsAsync"/> are C# default interface
|
||||
/// methods that throw <see cref="NotSupportedException"/> — drivers opt in by overriding so
|
||||
/// a raw-only driver compiles without forcing it to provide at-time / event surfaces it
|
||||
/// has no backend for. The sibling server-side surface, <see cref="IHistorianDataSource"/>,
|
||||
/// declares both methods as required because a registered historian owns the full read
|
||||
/// surface; the asymmetry is intentional.
|
||||
/// </remarks>
|
||||
public interface IHistoryProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Read raw historical samples for a single attribute over a time range.
|
||||
/// The Core wraps this with continuation-point handling.
|
||||
/// </summary>
|
||||
/// <param name="fullReference">The full reference path to the attribute.</param>
|
||||
/// <param name="startUtc">Inclusive lower bound on the time range (UTC).</param>
|
||||
/// <param name="endUtc">Exclusive upper bound on the time range (UTC).</param>
|
||||
/// <param name="maxValuesPerNode">Maximum number of values to return per node.</param>
|
||||
/// <param name="cancellationToken">A cancellation token to stop the operation.</param>
|
||||
/// <returns>A task that returns the history read result containing samples and optional continuation point.</returns>
|
||||
Task<HistoryReadResult> ReadRawAsync(
|
||||
string fullReference,
|
||||
DateTime startUtc,
|
||||
DateTime endUtc,
|
||||
uint maxValuesPerNode,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Read processed (aggregated) samples — interval-bucketed average / min / max / etc.
|
||||
/// Optional — drivers that only support raw history can throw <see cref="NotSupportedException"/>.
|
||||
/// </summary>
|
||||
/// <param name="fullReference">The full reference path to the attribute.</param>
|
||||
/// <param name="startUtc">Inclusive lower bound on the time range (UTC).</param>
|
||||
/// <param name="endUtc">Exclusive upper bound on the time range (UTC).</param>
|
||||
/// <param name="interval">The time interval for bucketing samples.</param>
|
||||
/// <param name="aggregate">The aggregate function to apply to each bucket.</param>
|
||||
/// <param name="cancellationToken">A cancellation token to stop the operation.</param>
|
||||
/// <returns>A task that returns the history read result containing aggregated samples and optional continuation point.</returns>
|
||||
Task<HistoryReadResult> ReadProcessedAsync(
|
||||
string fullReference,
|
||||
DateTime startUtc,
|
||||
DateTime endUtc,
|
||||
TimeSpan interval,
|
||||
HistoryAggregateType aggregate,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Read one sample per requested timestamp — OPC UA HistoryReadAtTime service. The
|
||||
/// driver interpolates (or returns the prior-boundary sample) when no exact match
|
||||
/// exists. Optional; drivers that can't interpolate throw <see cref="NotSupportedException"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Default implementation throws. Drivers opt in by overriding; keeps existing
|
||||
/// <c>IHistoryProvider</c> implementations compiling without forcing a ReadAtTime path
|
||||
/// they may not have a backend for.
|
||||
/// </remarks>
|
||||
/// <param name="fullReference">The full reference path to the attribute.</param>
|
||||
/// <param name="timestampsUtc">The list of timestamps at which to read values.</param>
|
||||
/// <param name="cancellationToken">A cancellation token to stop the operation.</param>
|
||||
/// <returns>A task that returns the history read result containing samples at the requested times.</returns>
|
||||
Task<HistoryReadResult> ReadAtTimeAsync(
|
||||
string fullReference,
|
||||
IReadOnlyList<DateTime> timestampsUtc,
|
||||
CancellationToken cancellationToken)
|
||||
=> throw new NotSupportedException(
|
||||
$"{GetType().Name} does not implement ReadAtTimeAsync. " +
|
||||
"Drivers whose backends support at-time reads override this method.");
|
||||
|
||||
/// <summary>
|
||||
/// Read historical alarm/event records — OPC UA HistoryReadEvents service. Distinct
|
||||
/// from the live event stream — historical rows come from an event historian (Galaxy's
|
||||
/// Alarm Provider history log, etc.) rather than the driver's active subscription.
|
||||
/// </summary>
|
||||
/// <param name="sourceName">
|
||||
/// Optional filter: null means "all sources", otherwise restrict to events from that
|
||||
/// source-object name. Drivers may ignore the filter if the backend doesn't support it.
|
||||
/// </param>
|
||||
/// <param name="startUtc">Inclusive lower bound on <c>EventTimeUtc</c>.</param>
|
||||
/// <param name="endUtc">Exclusive upper bound on <c>EventTimeUtc</c>.</param>
|
||||
/// <param name="maxEvents">
|
||||
/// Upper cap on returned events — the driver's backend enforces this. The type is
|
||||
/// <see cref="int"/> rather than <see cref="uint"/> (which the sibling raw / processed
|
||||
/// reads use for <c>maxValuesPerNode</c>) because callers and downstream historian
|
||||
/// adapters historically treat <c>maxEvents <= 0</c> as a sentinel meaning
|
||||
/// "use the backend's default cap" (see <c>WonderwareHistorianClient</c> /
|
||||
/// <c>HistorianDataSource</c>). The asymmetry is intentional.
|
||||
///
|
||||
/// <b>Continuation contract when using the sentinel:</b> When <c>maxEvents <= 0</c>
|
||||
/// the backend applies its own cap. If the backend's cap truncates the result
|
||||
/// (i.e. more matching events exist beyond the returned set), implementations MUST set
|
||||
/// <see cref="HistoricalEventsResult.ContinuationPoint"/> to a non-null token so
|
||||
/// callers can detect truncation and page. A null <c>ContinuationPoint</c> means all
|
||||
/// matching events were returned — callers rely on this to decide whether to page.
|
||||
/// </param>
|
||||
/// <param name="cancellationToken">Request cancellation.</param>
|
||||
/// <returns>A task that returns the historical events result containing matching event records and optional continuation point.</returns>
|
||||
/// <remarks>
|
||||
/// Default implementation throws. Drivers whose backend can serve historical events
|
||||
/// override: Galaxy (Wonderware Alarm & Events log) and the OPC UA Client driver
|
||||
/// (forwarding HistoryReadEvents to its upstream server). Raw-only drivers such as Modbus
|
||||
/// stay with the default and let callers see <c>BadHistoryOperationUnsupported</c>.
|
||||
///
|
||||
/// Note the type asymmetry with <see cref="ReadRawAsync"/> /
|
||||
/// <see cref="ReadProcessedAsync"/> (both use <c>uint maxValuesPerNode</c>): event
|
||||
/// readers accept a signed <c>int maxEvents</c> so callers can pass 0 / negative as a
|
||||
/// "use default cap" sentinel without an extra parameter or overload.
|
||||
/// </remarks>
|
||||
Task<HistoricalEventsResult> ReadEventsAsync(
|
||||
string? sourceName,
|
||||
DateTime startUtc,
|
||||
DateTime endUtc,
|
||||
int maxEvents,
|
||||
CancellationToken cancellationToken)
|
||||
=> throw new NotSupportedException(
|
||||
$"{GetType().Name} does not implement ReadEventsAsync. " +
|
||||
"Drivers whose backends have an event historian override this method.");
|
||||
}
|
||||
|
||||
/// <summary>Result of a HistoryRead call.</summary>
|
||||
/// <param name="Samples">Returned samples in chronological order.</param>
|
||||
/// <param name="ContinuationPoint">Opaque token for the next call when more samples are available; null when complete.</param>
|
||||
public sealed record HistoryReadResult(
|
||||
IReadOnlyList<DataValueSnapshot> Samples,
|
||||
byte[]? ContinuationPoint);
|
||||
|
||||
/// <summary>Aggregate function for processed history reads. Mirrors OPC UA Part 13 standard aggregates.</summary>
|
||||
/// <remarks>
|
||||
/// The first five members are the original set every historian backend serves. The remainder (#285) are
|
||||
/// the further standard Part 13 aggregates a server-side HistoryRead can forward to a backend that
|
||||
/// supports them (the OPC UA Client driver forwards all of them upstream). A backend that cannot serve a
|
||||
/// member must throw <see cref="HistoryAggregateNotSupportedException"/> — the server maps that to
|
||||
/// <c>BadAggregateNotSupported</c> rather than a generic failure. <b>Append only</b>: the numeric values
|
||||
/// are not persisted, but every <c>switch</c> over this enum must keep a total default.
|
||||
/// </remarks>
|
||||
public enum HistoryAggregateType
|
||||
{
|
||||
Average,
|
||||
Minimum,
|
||||
Maximum,
|
||||
Total,
|
||||
Count,
|
||||
Interpolative,
|
||||
TimeAverage,
|
||||
Start,
|
||||
End,
|
||||
Delta,
|
||||
Range,
|
||||
MinimumActualTime,
|
||||
MaximumActualTime,
|
||||
StartBound,
|
||||
EndBound,
|
||||
DeltaBounds,
|
||||
DurationGood,
|
||||
DurationBad,
|
||||
PercentGood,
|
||||
PercentBad,
|
||||
WorstQuality,
|
||||
NumberOfTransitions,
|
||||
AnnotationCount,
|
||||
StandardDeviationPopulation,
|
||||
VariancePopulation,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thrown by a history backend when it cannot serve the requested <see cref="HistoryAggregateType"/>.
|
||||
/// The Server HistoryRead path translates it to <c>BadAggregateNotSupported</c> for that node only.
|
||||
/// </summary>
|
||||
public sealed class HistoryAggregateNotSupportedException : NotSupportedException
|
||||
{
|
||||
/// <summary>The aggregate the backend could not serve.</summary>
|
||||
public HistoryAggregateType Aggregate { get; }
|
||||
|
||||
/// <summary>Creates the exception for <paramref name="aggregate"/>.</summary>
|
||||
/// <param name="aggregate">The unsupported aggregate.</param>
|
||||
public HistoryAggregateNotSupportedException(HistoryAggregateType aggregate)
|
||||
: base($"The historian backend does not support the {aggregate} aggregate.")
|
||||
=> Aggregate = aggregate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thrown by a history backend that is a passthrough to another OPC UA server when that server answered
|
||||
/// a node with a Bad per-node <c>HistoryReadResult.StatusCode</c> other than
|
||||
/// <c>BadAggregateNotSupported</c> (which has its own <see cref="HistoryAggregateNotSupportedException"/>).
|
||||
/// The Server HistoryRead path surfaces <see cref="StatusCode"/> verbatim as that node's result status
|
||||
/// instead of the generic <c>BadHistoryOperationUnsupported</c>, so a refused read is never mistaken for
|
||||
/// an empty Good one.
|
||||
/// </summary>
|
||||
public sealed class HistoryReadStatusException : Exception
|
||||
{
|
||||
/// <summary>The raw OPC UA status code the upstream server returned for the node.</summary>
|
||||
public uint StatusCode { get; }
|
||||
|
||||
/// <summary>Creates the exception for the upstream <paramref name="statusCode"/>.</summary>
|
||||
/// <param name="statusCode">The Bad OPC UA status code to surface for the node.</param>
|
||||
/// <exception cref="ArgumentException"><paramref name="statusCode"/> is not a Bad code (severity bits <c>10</c>/<c>11</c>).</exception>
|
||||
public HistoryReadStatusException(uint statusCode)
|
||||
: base(IsBad(statusCode)
|
||||
? $"The upstream server refused the history read with status 0x{statusCode:X8}."
|
||||
: throw new ArgumentException($"0x{statusCode:X8} is not a Bad status code.", nameof(statusCode)))
|
||||
=> StatusCode = statusCode;
|
||||
|
||||
private static bool IsBad(uint statusCode) => (statusCode & 0x80000000u) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// One row returned by <see cref="IHistoryProvider.ReadEventsAsync"/> — a historical
|
||||
/// alarm/event record, not the OPC UA live-event stream. Fields match the minimum set the
|
||||
/// Server needs to populate a <c>HistoryEventFieldList</c> for HistoryReadEvents responses.
|
||||
/// </summary>
|
||||
/// <param name="EventId">Stable unique id for the event — driver-specific format.</param>
|
||||
/// <param name="SourceName">Source object that emitted the event. May differ from the <c>sourceName</c> filter the caller passed (fuzzy matches).</param>
|
||||
/// <param name="EventTimeUtc">Process-side timestamp — when the event actually occurred.</param>
|
||||
/// <param name="ReceivedTimeUtc">Historian-side timestamp — when the historian persisted the row; may lag <paramref name="EventTimeUtc"/> by the historian's buffer flush cadence.</param>
|
||||
/// <param name="Message">Human-readable message text.</param>
|
||||
/// <param name="Severity">OPC UA severity (1-1000). Drivers map their native priority scale onto this range.</param>
|
||||
public sealed record HistoricalEvent(
|
||||
string EventId,
|
||||
string? SourceName,
|
||||
DateTime EventTimeUtc,
|
||||
DateTime ReceivedTimeUtc,
|
||||
string? Message,
|
||||
ushort Severity);
|
||||
|
||||
/// <summary>Result of a <see cref="IHistoryProvider.ReadEventsAsync"/> call.</summary>
|
||||
/// <param name="Events">Events in chronological order by <c>EventTimeUtc</c>.</param>
|
||||
/// <param name="ContinuationPoint">Opaque token for the next call when more events are available; null when complete.</param>
|
||||
public sealed record HistoricalEventsResult(
|
||||
IReadOnlyList<HistoricalEvent> Events,
|
||||
byte[]? ContinuationPoint);
|
||||
@@ -81,7 +81,6 @@ public sealed record DriverResilienceOptions
|
||||
[DriverCapability.Probe] = new(TimeoutSeconds: 2, RetryCount: 3, BreakerFailureThreshold: 5),
|
||||
[DriverCapability.AlarmSubscribe] = new(TimeoutSeconds: 5, RetryCount: 3, BreakerFailureThreshold: 5),
|
||||
[DriverCapability.AlarmAcknowledge] = new(TimeoutSeconds: 5, RetryCount: 0, BreakerFailureThreshold: 5),
|
||||
[DriverCapability.HistoryRead] = new(TimeoutSeconds: 30, RetryCount: 2, BreakerFailureThreshold: 5),
|
||||
// Initialize: no retry (the DriverInstanceActor reconnect timer is the retry loop) and a
|
||||
// threshold of 2, NOT the Read-sized 5 — the breaker samples over 30 s and the reconnect
|
||||
// timer fires every 10 s, so at most three init attempts fit one window; a threshold of 5
|
||||
@@ -97,7 +96,6 @@ public sealed record DriverResilienceOptions
|
||||
[DriverCapability.Probe] = new(TimeoutSeconds: 4, RetryCount: 3, BreakerFailureThreshold: 5),
|
||||
[DriverCapability.AlarmSubscribe] = new(TimeoutSeconds: 8, RetryCount: 3, BreakerFailureThreshold: 5),
|
||||
[DriverCapability.AlarmAcknowledge] = new(TimeoutSeconds: 8, RetryCount: 0, BreakerFailureThreshold: 5),
|
||||
[DriverCapability.HistoryRead] = new(TimeoutSeconds: 60, RetryCount: 2, BreakerFailureThreshold: 5),
|
||||
// See the Tier A Initialize note: threshold 2 is cadence-reachability, not a tuning choice.
|
||||
[DriverCapability.Initialize] = new(TimeoutSeconds: 8, RetryCount: 0, BreakerFailureThreshold: 2),
|
||||
},
|
||||
@@ -110,7 +108,6 @@ public sealed record DriverResilienceOptions
|
||||
[DriverCapability.Probe] = new(TimeoutSeconds: 10, RetryCount: 1, BreakerFailureThreshold: 0),
|
||||
[DriverCapability.AlarmSubscribe] = new(TimeoutSeconds: 15, RetryCount: 1, BreakerFailureThreshold: 0),
|
||||
[DriverCapability.AlarmAcknowledge] = new(TimeoutSeconds: 15, RetryCount: 0, BreakerFailureThreshold: 0),
|
||||
[DriverCapability.HistoryRead] = new(TimeoutSeconds: 120, RetryCount: 1, BreakerFailureThreshold: 0),
|
||||
// Tier C stays breaker-less like every other Tier C capability (process-level supervision).
|
||||
[DriverCapability.Initialize] = new(TimeoutSeconds: 15, RetryCount: 0, BreakerFailureThreshold: 0),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user