fix(historian): gateway AGGREGATE_NOT_SUPPORTED_FOR_TAG -> BadAggregateNotSupported, no health failure (#900)
v2-ci / unit-tests (push) Has been cancelled
v2-ci / unit-tests (push) Has been cancelled
A node declared numeric whose AVEVA tag is discrete (or string) is not refused up front by #897, so its native TimeAverage / Total still reach AVEVA, which refuses them. A HistorianGateway at or after historiangw#135 (a220ec0) answers that FailedPrecondition with a google.rpc.ErrorInfo (reason AGGREGATE_NOT_SUPPORTED_FOR_TAG, domain historian_gateway.v1) in grpc-status-details-bin. The Gateway backend's native processed read now maps exactly that to HistoryAggregateNotSupportedException (BadAggregateNotSupported for the node) without recording a health failure or treating it as transport. Any other FailedPrecondition and every Internal (an older gateway's answer for the same case) keep today's handling. Detection lives in one place, GatewayErrorReasons. The HistorianGateway.Client pin stays 1.0.0 (1.1.0 is not on the feed): 1.0.0 keeps the gateway's RpcException, trailers included, as the InnerException of the HistorianGatewayException it throws, so the reason is read with Grpc.StatusProto 2.76.0 (added via central package management; nuget.org; same version as Grpc.Core.Api; depends on Google.Api.CommonProtos 2.16.0, which accepts Google.Protobuf 3.34.1). With Client 1.1.0, ReasonOf becomes `(ex as HistorianGatewayException)?.ErrorReason` and the StatusProto reference can go. Tests: - GatewayAggregateNotSupportedWireTests: in-process gRPC gateway sending the real status + ErrorInfo shape (copied from historiangw HistorianRpcFaults.ToRpcException), read through the PUBLISHED Client 1.0.0 via the production adapter: TimeAverage / Total -> HistoryAggregateNotSupportedException, 0 health failures; controls (Internal; FailedPrecondition without ErrorInfo; other reason; other domain; the reason on Internal) are rethrown, not IOException, and counted; positive control served. - DiscreteTagTimeWeightedWireTests: over a real SDK session, a Double / Int32 node on the discrete tag -> BadAggregateNotSupported with 0 failures; the older gateway's Internal answer keeps its status and counts. - AvevaGatewayAggregateRefusalLiveGateTests: opt-in read-only live gate (OTOPCUA_AVEVA_GATE_900=true). Mutations (catch removed; status ignored; domain ignored; reason ignored; inner-exception walk removed) each fail at least one test. Release build 0 warnings; Historian.Gateway.Tests 601/607 (6 skipped), OpcUaServer IntegrationTests 323/361 (38 skipped). Live, read-only: private HistorianGateway container from a220ec0 (API-key auth disabled, live writes off, removed after) against wonder-sql-vd03: Double and Int32 nodes on SysPulse, TimeAverage and Total -> BadAggregateNotSupported, health failures unchanged (0); SysTimeSec TimeAverage Good (2 rows); SysPulse Average served. Gateway traffic was ReadAggregate / ReadRaw / GetTagMetadata only. Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
This commit is contained in:
+8
-3
@@ -1592,9 +1592,14 @@ serve a Boolean node's numeric aggregates as 0/1 (stepped for the time-weighted
|
||||
true), and the Gateway's raw-evaluated Average/Min/Max/… do the same. So on a Boolean node TimeAverage / Total are served by
|
||||
`SqlServer` / `QuestDb` and refused by `Gateway`. An unknown node type (BaseDataType, an array) is not refused.
|
||||
|
||||
The residual case — a node declared numeric whose AVEVA tag is discrete — still reaches AVEVA and answers
|
||||
`BadHistoryOperationUnsupported`, counted as a failure, until the gateway surfaces AVEVA's native error code as a
|
||||
deterministic status.
|
||||
The residual case — a node declared numeric whose AVEVA tag is discrete (or string) — still reaches AVEVA. A gateway at or
|
||||
after historiangw#135 (`a220ec0`) answers it `FailedPrecondition` with a `google.rpc.ErrorInfo` (reason
|
||||
`AGGREGATE_NOT_SUPPORTED_FOR_TAG`, domain `historian_gateway.v1`) in `grpc-status-details-bin`; the Gateway backend maps that
|
||||
to **`BadAggregateNotSupported`** for the node and counts **no** backend failure (#900, live-verified read-only against
|
||||
wonder-sql-vd03 `SysPulse`). Only that reason is matched: any other `FailedPrecondition` and every `Internal` keep the old
|
||||
handling, so an older gateway (which answers this case `Internal`) still gives `BadHistoryOperationUnsupported`, counted as a
|
||||
failure. The reason is read in one place, `GatewayErrorReasons` — from the trailer via `Grpc.StatusProto` while the
|
||||
HistorianGateway Client pin is 1.0.0; with Client 1.1.0 it becomes `HistorianGatewayException.ErrorReason`.
|
||||
|
||||
⚠️ **`Server/ServerCapabilities/AggregateFunctions` (`i=2997`) is deliberately NOT trimmed** — it keeps the SDK's full 37 in
|
||||
every configuration, including with no historian. Part 5 defines it as every aggregate the *server* supports, and the server
|
||||
|
||||
Reference in New Issue
Block a user