SendEvent over gRPC: implement + live-validate (was capture-gated)
Captured the native 2023 R2 client's gRPC event send (new capture-send-event harness scenario): it rides HistoryService.AddStreamValues with the SAME "OS" (0x534F) storage-sample buffer the WCF path already uses (HistorianEventWrite- Protocol) — confirming "no distinct RPC", and that it is NOT the historical write's "ON" buffer. Diffed the write-enabled vs read-only v8 Event open: byte- identical apart from per-session crypto, so the existing OpenSession event path is reused unchanged. So SendEvent-over-gRPC was pure assembly of proven parts: - HistorianGrpcEventWriteOrchestrator = v8 Event open + CM_EVENT registration (UpdC3/RegisterTags/EnsureTags) + AddStreamValues(OS buffer). - HistorianClient.SendEventAsync now routes to it for RemoteGrpc (WCF otherwise). Live-validated end-to-end against the 2023 R2 server: pure-managed SDK send → AddStreamValues BSuccess=true → the event reads back from the server (markers confirmed in returned event rows). The native gRPC RegisterTags(24B) + EnsureTags(86B) byte-match our serializers (new GrpcEventSendProtocolTests golden, closing the 83-vs-86 EnsureTags question). Gated live test SendEventAsync_OverGrpc_AcceptsEvent (opt-in HISTORIAN_GRPC_EVENT_SEND=1). 331 offline tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B6mcaT2PjRFKcogzp9UkfC
This commit is contained in:
@@ -76,7 +76,17 @@ reuses the proven 2020 WCF byte serializers/parsers unchanged inside protobuf
|
||||
`capture-event` harness (native, returns rows).
|
||||
2. **R4.3 active-SF magnitude** — needs an **SF-active server** (D2 storage-engine
|
||||
console handle).
|
||||
3. **SendEvent over gRPC** — **capture-gated**: no distinct RPC, framing uncaptured.
|
||||
3. **SendEvent over gRPC** — ✅ **SHIPPED + LIVE-VALIDATED 2026-06-23.** `SendEventAsync`
|
||||
now routes over `RemoteGrpc` (`HistorianGrpcEventWriteOrchestrator`). Captured the native
|
||||
client live (`capture-send-event` harness scenario): the send rides
|
||||
`HistoryService.AddStreamValues` with the **same "OS" (0x534F) buffer the WCF path uses**
|
||||
(`HistorianEventWriteProtocol` — "no distinct RPC" confirmed true), on a v8 Event session +
|
||||
CM_EVENT registration. The write-enabled Event open is **byte-identical** to the read-only one
|
||||
(diffed live — only per-session crypto differs), so the existing event-open path is reused
|
||||
unchanged. End-to-end: pure-managed SDK send → `BSuccess=true` → event read back from the live
|
||||
server (markers `SdkSendProbe`/`SdkCaptureProbe` confirmed in returned rows). Golden-tested
|
||||
(`GrpcEventSendProtocolTests`) + gated live test (`SendEventAsync_OverGrpc_AcceptsEvent`,
|
||||
opt-in `HISTORIAN_GRPC_EVENT_SEND=1`).
|
||||
4. **ExecuteSqlCommand over gRPC** — **server-walled** (`CSrvDbConnection`;
|
||||
RegisterTags prime doesn't help). Use WCF for SQL.
|
||||
5. **R4.2 revision EDITS** — storage-engine-pipe-only on BOTH transports (the D2 wall).
|
||||
@@ -123,18 +133,17 @@ with these refinements:
|
||||
would differ is native and not on the wire. One untested low-effort check
|
||||
remains: byte-diff a captured **Event-connection** EnsureTags/RegisterTags
|
||||
against our replay (the 83-vs-86-byte EnsT gap was never actually compared).
|
||||
- **Item 3 (SendEvent over gRPC)** — **sharpened from "maybe no RPC" to a precise
|
||||
capture.** RPC **confirmed** = `HistoryService.AddStreamValues` (the "no distinct
|
||||
RPC" note is TRUE; an event rides the same RPC as a streamed sample, discriminated
|
||||
inside `btValues`). Public API `HistorianAccess.AddStreamedValue(HistorianEvent)`
|
||||
→ native `AddHistorianValue`; prereqs known (write-enabled Event conn, CM_EVENT
|
||||
tag handle, quality 192); field set/order recovered from `HistorianEvent.PackToVtq`.
|
||||
**Only the `btValues` VTQ byte layout is missing** — built by native
|
||||
`CCommonArchestraEventValue::PackToVtq` and copied out as an opaque `CDataChunk`.
|
||||
Our read parser already decodes the inverse property-bag format. **Capturable
|
||||
against the local Historian** (instrument `PackToVtq` output / the `AddStreamValues`
|
||||
body) → then build `HistorianEventWriteProtocol` and reuse the
|
||||
`HistorianGrpcHistoricalWriteOrchestrator` plumbing.
|
||||
- **Item 3 (SendEvent over gRPC)** — ✅ **SHIPPED + LIVE-VALIDATED 2026-06-23** (was
|
||||
"capturable"). RPC confirmed = `HistoryService.AddStreamValues` (the "no distinct RPC"
|
||||
note is TRUE). The `btValues` VTQ buffer turned out to be already-owned: our M2
|
||||
`HistorianEventWriteProtocol.SerializeAddStreamValuesBuffer` ("OS" buffer, decoded from
|
||||
the WCF event-send) is the transport-independent `PackToVtq` equivalent and the gRPC send
|
||||
uses it **verbatim** (live capture: sig `OS`/0x534F, CM_EVENT GUID, identical framing — NOT
|
||||
the historical write's "ON" buffer). The write-enabled Event open is byte-identical to the
|
||||
read-only one (live diff). So SendEvent-over-gRPC was pure assembly:
|
||||
`HistorianGrpcEventWriteOrchestrator` = existing v8 Event open + existing CM_EVENT
|
||||
registration + `AddStreamValues`(OS buffer). End-to-end live-validated (send → `BSuccess`
|
||||
→ read back from the live server). Golden-tested + gated live test.
|
||||
- **Item 4 (ExecuteSql over gRPC)** — **confirmed walled + explained.** The stock
|
||||
client gates SQL **out client-side**: `HistorianAccess.ExecuteSqlCommand` returns
|
||||
`OperationNotSupported` when `IsManagedHistorian(node)` or `!IsProcessConnectionRequested()`
|
||||
|
||||
Reference in New Issue
Block a user