From 64c9793b91ed757d2a1ab3c81f9c547d325002c2 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 25 Jun 2026 17:00:50 -0400 Subject: [PATCH] =?UTF-8?q?docs(c2):=20correct=20event-read=20gating=20mes?= =?UTF-8?q?sages=20=E2=80=94=20WCF=20not=20served=20on=202023=20R2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gRPC ReadEvents throws no longer advise "use the WCF transport for event reads": that path is moot on 2023 R2 (net.tcp is reset at the framing layer, live-disproven 2026-06-25). Messages now state event reads are auth-solved but server-gated over gRPC and have no WCF fallback on 2023 R2, citing the two evidence docs. WCF orchestrator remarks scoped to legacy 2020 historians; row layout noted as decoded. String/comment only; throw behavior unchanged. Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii --- .../Grpc/HistorianGrpcEventOrchestrator.cs | 38 +++++++++++-------- .../Wcf/HistorianWcfEventOrchestrator.cs | 10 +++-- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/AVEVA.Historian.Client/Grpc/HistorianGrpcEventOrchestrator.cs b/src/AVEVA.Historian.Client/Grpc/HistorianGrpcEventOrchestrator.cs index 1720728..74e8568 100644 --- a/src/AVEVA.Historian.Client/Grpc/HistorianGrpcEventOrchestrator.cs +++ b/src/AVEVA.Historian.Client/Grpc/HistorianGrpcEventOrchestrator.cs @@ -34,13 +34,16 @@ namespace AVEVA.Historian.Client.Grpc; /// (the read path proved the front-door session is sufficient over gRPC). /// /// -/// Live status (2026-06-22): the chain runs end-to-end and StartEventQuery succeeds, but -/// GetNextEventQueryResultBuffer long-polls when the query has no rows — it blocks to the -/// call deadline instead of returning the synchronous 5-byte code-85 terminal the 2020 WCF op returns. -/// A poll-deadline expiry is therefore treated as the no-data terminal (see the loop). The idle dev box -/// holds no events, so row-level retrieval is not yet live-verified; verifying parsed rows over -/// gRPC awaits an event-bearing 2023 R2 server. This is tooled + completes cleanly, NOT proven to -/// return rows. +/// Live status — server-gated (settled 2026-06-25): the chain runs end-to-end and +/// StartEventQuery succeeds, but GetNextEventQueryResultBuffer long-polls to the +/// no-data terminal (instead of the synchronous 5-byte code-85 terminal the 2020 WCF op returns); a +/// poll-deadline expiry is treated as that terminal (see the loop). This is not an empty-box +/// artifact: the live 2023 R2 server holds tens of thousands of events yet scopes 0 rows to a +/// managed connection. Every client-controllable layer was byte-matched to the stock client that returns +/// rows (see docs/reverse-engineering/grpc-event-query-capture.md) — the gate is a server-internal +/// per-connection retrieval working-set, not client-fixable. The legacy WCF transport is not a +/// fallback on 2023 R2 (docs/reverse-engineering/wcf-event-read-spike-results.md). Tooled + +/// completes cleanly, but proven NOT to return rows over a managed connection. /// /// internal sealed class HistorianGrpcEventOrchestrator @@ -101,9 +104,11 @@ internal sealed class HistorianGrpcEventOrchestrator { throw new ProtocolEvidenceMissingException( $"ReadEvents over gRPC did not return rows within {OverallBudget.TotalSeconds:0}s: StartEventQuery " + - "succeeds but the CM_EVENT registration replay stalls and GetNextEventQueryResultBuffer long-polls " + - "(no synchronous code-85 terminal over gRPC). Row-level retrieval is not yet verified over gRPC " + - "(the dev box holds no events) — use the WCF transport for event reads."); + "succeeds but GetNextEventQueryResultBuffer long-polls to the no-data terminal. Event-row retrieval " + + "over gRPC is auth-solved but server-gated — the 2023 R2 server scopes 0 rows to a managed connection " + + "(see docs/reverse-engineering/grpc-event-query-capture.md). The legacy WCF transport is NOT a fallback " + + "on 2023 R2 (live-disproven 2026-06-25: net.tcp is reset at the framing layer — see " + + "docs/reverse-engineering/wcf-event-read-spike-results.md), so there is no event-read path on a 2023 R2 historian."); } foreach (HistorianEvent evt in events) @@ -169,16 +174,19 @@ internal sealed class HistorianGrpcEventOrchestrator // reaches the no-data terminal with ZERO rows (the gRPC server long-polls GetNext rather than // returning the WCF code-85 terminal), we cannot distinguish "genuinely no events in range" // from "the CM_EVENT registration replay didn't fully land over gRPC" — so we refuse to return - // a possibly-false empty list and surface the unverified state instead. An event-bearing 2023 R2 - // server will return rows here and exercise the parse path; flip this once that is captured. + // a possibly-false empty list and surface the gated state instead. Proven server-gated: the live + // 2023 R2 server holds tens of thousands of events yet scopes 0 to a managed gRPC connection + // (grpc-event-query-capture.md); WCF is not a 2023 R2 fallback (wcf-event-read-spike-results.md). if (events.Count == 0) { throw new ProtocolEvidenceMissingException( "ReadEvents over gRPC: the chain completes and StartEventQuery succeeds, but " + "GetNextEventQueryResultBuffer returns no rows (it long-polls to the no-data terminal " + - $"after the CM_EVENT registration replay; last={LastErrorBufferDescription}). Row-level " + - "retrieval is not yet verified over gRPC (the dev box holds no events) — use the WCF " + - "transport for event reads until a capture against an event-bearing 2023 R2 server confirms it."); + $"after the CM_EVENT registration replay; last={LastErrorBufferDescription}). Event-row retrieval " + + "over gRPC is auth-solved but server-gated — the 2023 R2 server scopes 0 rows to a managed connection " + + "(see docs/reverse-engineering/grpc-event-query-capture.md). The legacy WCF transport is NOT a fallback " + + "on 2023 R2 (live-disproven 2026-06-25: net.tcp is reset at the framing layer — see " + + "docs/reverse-engineering/wcf-event-read-spike-results.md)."); } return events; diff --git a/src/AVEVA.Historian.Client/Wcf/HistorianWcfEventOrchestrator.cs b/src/AVEVA.Historian.Client/Wcf/HistorianWcfEventOrchestrator.cs index 779a5a2..c8f81a5 100644 --- a/src/AVEVA.Historian.Client/Wcf/HistorianWcfEventOrchestrator.cs +++ b/src/AVEVA.Historian.Client/Wcf/HistorianWcfEventOrchestrator.cs @@ -9,9 +9,13 @@ namespace AVEVA.Historian.Client.Wcf; /// /// Mirrors HistorianWcfReadOrchestrator but targets IRetrievalServiceContract4 for the event flow. -/// Event row buffer layout is undecoded as of this pass — when StartEventQuery succeeds, this -/// orchestrator returns an empty enumeration but logs the row-buffer length via the -/// diagnostic so a follow-up capture can decode the wire shape. +/// Applies to legacy 2020-era WCF (net.tcp) historians only. The event row-buffer layout is now +/// decoded (; verified against real captured rows). Note: a +/// 2023 R2 historian does NOT serve this WCF transport at all — net.tcp is reset at the framing +/// layer before any auth (live-disproven 2026-06-25; see +/// docs/reverse-engineering/wcf-event-read-spike-results.md), so this orchestrator is not a +/// fallback for 2023 R2 deployments. The native return codes 76/85 noted below were 2020-historian +/// observations. /// internal sealed class HistorianWcfEventOrchestrator {