docs(grpc): matrix + plan reflect ext-prop fix, SQL prime result, ConnStatus
- README transport matrix: GetTagExtendedProperties notes the multi-property parser fix; AddTagExtendedProperties read-back now round-trips; GetConnectionStatus gRPC -> live-verified; ExecuteSqlCommand notes the RegisterTags prime does not help. Refresh the closing production-pattern guidance. - grpc-tooling-completion.md: mark #5 (ConnStatus) done, #4 (SQL prime) negative, and the #1 ext-prop read-back follow-up done. 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:
@@ -86,13 +86,13 @@ request rides the RPC but the server faults on an unmet precondition) ·
|
||||
| `GetServerTimeZoneAsync` | ❌ | ✅ | 2020 `GetSystemTimeZoneName` is a client-side stub (empty); WCF throws |
|
||||
| `GetStoreForwardStatusAsync` | ⚠️ | ✅ | gRPC contacts the server (measured idle-state, reports `ErrorOccurred`); WCF returns synthesized all-false. Active-SF magnitude is D2-gated on both |
|
||||
| `GetRuntimeParameterAsync` | ✅ | ✅ | tooled + live-verified over gRPC (`StatusService.GetRuntimeParameter`, the 2020 `GETRP` buffers ride unchanged) |
|
||||
| `GetTagExtendedPropertiesAsync` | ✅ | ✅ | tooled + live-verified over gRPC (`RetrievalService.GetTagExtendedPropertiesFromName`, the `GetTepByNm` buffers ride unchanged) |
|
||||
| `ExecuteSqlCommandAsync` | ✅ | ⛔ | gRPC request rides `RetrievalService.ExecuteSqlCommand`, but the server-side `CSrvDbConnection.ExecuteSqlCommand` faults (`IndexOutOfRange`, native err 38) — an unmet DB-connection precondition; bounded behind `ProtocolEvidenceMissingException`. Use WCF |
|
||||
| `GetTagExtendedPropertiesAsync` | ✅ | ✅ | tooled + live-verified over gRPC (`RetrievalService.GetTagExtendedPropertiesFromName`, the `GetTepByNm` buffers ride unchanged). The shared parser now handles the live multi-property response shape (one group per property + a uint16 searchability-flags trailer), fixed 2026-06-22 |
|
||||
| `ExecuteSqlCommandAsync` | ✅ | ⛔ | gRPC request rides `RetrievalService.ExecuteSqlCommand`, but the server-side `CSrvDbConnection.ExecuteSqlCommand` faults (`IndexOutOfRange`, native err 38) — an unmet DB-connection precondition. A `HistoryService.RegisterTags` prime does **not** clear it (tried live 2026-06-22, both 0x402/0x401). Bounded behind `ProtocolEvidenceMissingException`. Use WCF |
|
||||
| `ReadEventsAsync` | ✅ | ⚠️ | tooled + routed over gRPC: the full CM_EVENT registration replay (`UpdateClientStatus`→`RegisterTags`→`EnsureTags` + discovery probes) runs and `StartEventQuery` succeeds, but `GetNextEventQueryResultBuffer` **long-polls** on no data (it blocks to the deadline rather than returning the synchronous 5-byte code-85 terminal the WCF op gives). The read is **hard-bounded** (≤30s) and throws `ProtocolEvidenceMissingException` on the no-row path rather than assert a false empty. Row-level retrieval is **not yet live-verified** — the dev box holds no events; pending a capture against an event-bearing 2023 R2 server. Use WCF for event reads |
|
||||
| `SendEventAsync` | ✅ | 🔌 | rides `AddStreamValues` family; no distinct event-send RPC, framing uncaptured over gRPC |
|
||||
| `EnsureTagAsync` / `DeleteTagAsync` / `RenameTagsAsync` | ✅ | ✅ | live-verified 2026-06-22 over gRPC (`HistoryService.EnsureTags` / `DeleteTags` / `StartJob`, write-enabled 0x401 session, WCF serializers reused) via a self-cleaning sandbox-tag lifecycle. Rename is an async StartJob — transiently rejectable right after create, so callers should retry |
|
||||
| `AddTagExtendedPropertiesAsync` | ✅ | ✅ | live-verified 2026-06-22 over gRPC (`HistoryService.AddTagExtendedProperties`, write-enabled session). NOTE: reading a written prop back via `GetTagExtendedPropertiesAsync` can hit a shared-parser evidence gap (value marker `0x01` vs the captured compact-string `0x09`); the write itself is confirmed. gRPC also exposes `DeleteTagExtendedProperties` (WCF delete was server-blocked) |
|
||||
| `GetConnectionStatusAsync` | ✅ | ❌ | synthesized from an authenticated probe — no dedicated RPC on either transport (gRPC `PingServer`/`GetHistorianConsoleStatus` could synthesize it) |
|
||||
| `AddTagExtendedPropertiesAsync` | ✅ | ✅ | live-verified 2026-06-22 over gRPC (`HistoryService.AddTagExtendedProperties`, write-enabled session); a written prop now round-trips through `GetTagExtendedPropertiesAsync` (the multi-property parser fix above). gRPC also exposes `DeleteTagExtendedProperties` (WCF delete was server-blocked) |
|
||||
| `GetConnectionStatusAsync` | ✅ | ✅ | live-verified 2026-06-22 over gRPC — measured from the handshake (`OpenConnection` yields a storage-session GUID ⇒ connected). No dedicated RPC on either transport; store-forward connectivity stays false (D2-gated) |
|
||||
| `ReadBlocksAsync` | ❌ | ❌ | `StartBlockRetrievalQuery` never captured on either transport — throws `ProtocolEvidenceMissingException` |
|
||||
|
||||
In short: **WCF is the broad, mature surface** (every config write, events, SQL,
|
||||
@@ -116,9 +116,10 @@ confirming rows awaits an event-bearing 2023 R2 server. The remaining 🔌 row
|
||||
into a gRPC orchestrator + live-capture), not protocol-discovery — but per
|
||||
"capture first, never guess wire bytes" it stays untooled until verified live. The
|
||||
natural production pattern today: `RemoteGrpc` now covers reads,
|
||||
`AddHistoricalValuesAsync`, and the tag-config writes (create/delete/rename/extended
|
||||
properties, live-verified) — use WCF for SQL, events, and reading extended
|
||||
properties back until those gRPC gaps close.
|
||||
`AddHistoricalValuesAsync`, the tag-config writes (create/delete/rename/extended
|
||||
properties, including read-back), and connection status — all live-verified. Use
|
||||
WCF for SQL (server-walled on gRPC) and event reads/sends (gRPC event rows are
|
||||
long-poll-blocked pending an event-bearing server).
|
||||
|
||||
> A 2023 R2 server reports History interface version 12 (vs. 11 on 2020). The
|
||||
> connect-time version gate accepts both — they are byte-compatible — so gRPC
|
||||
|
||||
@@ -50,9 +50,13 @@ transiently reject right after the create commits and on target-name collision
|
||||
pre-cleans both names and retries rename (4×); callers should likewise retry. (b) **reading a written
|
||||
extended property back** via `GetTagExtendedPropertiesAsync` hits a shared-parser evidence gap (value
|
||||
marker `0x01` where the parser expects compact-string `0x09`) — a read-side gap, not a write failure;
|
||||
the test tolerates it. Lifecycle test is self-cleaning and asserts no litter remains (verified two
|
||||
consecutive clean passes). Next read-side follow-up: capture the `0x01` extended-property value
|
||||
encoding and extend `HistorianTagExtendedPropertyProtocol.ParseResponse`.
|
||||
the test tolerates it. Lifecycle test is self-cleaning and best-effort cleans up (rename is async +
|
||||
the browse/metadata view is eventually consistent, so a hard absence assert would be racy).
|
||||
**Read-side follow-up DONE 2026-06-22:** captured the live `GetTagExtendedPropertiesFromName` bytes
|
||||
and fixed the parser — the response is one group per property (tag name repeats) with a **uint16
|
||||
searchability-flags trailer** per property (e.g. `0x0003` built-in, `0x0001` user-added), NOT the
|
||||
1-byte group trailer the old model assumed (which drifted one byte per group → `0x09`-vs-`0x01`). A
|
||||
written prop now round-trips end-to-end live; golden multi-group test added.
|
||||
|
||||
_Original notes:_
|
||||
- **Goal:** flip the 🧪 writes to ✅ by running the gated lifecycle test against a sandbox tag.
|
||||
@@ -115,18 +119,23 @@ _Original notes (still the reference for the registration replay):_
|
||||
"capture first, never guess"). Depends on #2 (same CM_EVENT registration).
|
||||
- **Risk:** high / blocked on capture. Lowest priority.
|
||||
|
||||
### 4. (Stretch) SQL server-wall investigation
|
||||
### 4. (Stretch) SQL server-wall investigation — ❌ RegisterTags prime does NOT help (2026-06-22)
|
||||
- `ExecuteSqlCommand` over gRPC faults server-side in `CSrvDbConnection.ExecuteSqlCommand`
|
||||
(IndexOutOfRange / native err 38) — a DB-connection precondition the managed session
|
||||
doesn't establish. Next avenue: try a `HistoryService.RegisterTags`-family prime before
|
||||
`ExecuteSqlCommand` (same fix that unblocked the M3 write path / OpenStorageConnection
|
||||
class of wall). If it works, replace the bounded throw in `HistorianGrpcSqlClient` with
|
||||
the real GetNextQueryResultBuffer fetch loop (already written there) and flip the test.
|
||||
(IndexOutOfRange / native err 38). Tried the `HistoryService.RegisterTags`-family prime
|
||||
before `ExecuteSqlCommand` on both read-only (0x402) and write-enabled (0x401) sessions:
|
||||
it does **not** clear the wall — `RegisterTags` itself returned false and `ExecuteSqlCommand`
|
||||
faulted with the identical native-38 error (decoded buffer: `...CSrvDbConnection.ExecuteSqlCommand
|
||||
... System.IndexOutOfRangeException`). So unlike OpenStorageConnection, the SQL DB-connection
|
||||
context is NOT established by the RegisterTags family. The op stays bounded behind
|
||||
`ProtocolEvidenceMissingException`; use WCF for SQL. Remaining avenues are deeper (reproduce
|
||||
the server-side DB connection-string/index setup the native client triggers) — low priority.
|
||||
|
||||
### 5. (Optional) GetConnectionStatus over gRPC
|
||||
- Currently WCF-only, synthesized from an authenticated probe (no dedicated RPC either
|
||||
transport). Could synthesize the same over gRPC via `StatusService.PingServer` /
|
||||
`GetHistorianConsoleStatus`. Low value; do only if parity is wanted.
|
||||
### 5. GetConnectionStatus over gRPC — ✅ DONE 2026-06-22
|
||||
- `HistorianGrpcStatusClient.GetConnectionStatusAsync` synthesizes the status from a measured
|
||||
gRPC handshake (OpenConnection yielding a storage-session GUID ⇒ connected), mirroring the WCF
|
||||
synthesize-from-probe approach. Routed in `Historian2020ProtocolDialect` on `UseGrpc` (the WCF
|
||||
path used the MDAS binding, which can't reach the gRPC port). Live-verified; store-forward
|
||||
connectivity stays false (D2-gated). Gated test `GetConnectionStatusAsync_OverGrpc_ReportsConnected`.
|
||||
|
||||
### Out of scope
|
||||
- `ReadBlocks` (`StartBlockRetrievalQuery`) — never captured on either transport; leave
|
||||
|
||||
Reference in New Issue
Block a user