M3 probe: non-streamed write transaction reachable over 2023 R2 gRPC (Begin/End live-verified)

The D2 storage-engine-pipe wall is WCF-transport-specific. On the 2023 R2 gRPC
front door, TransactionService is a first-class service AND the gateway to the
storage engine, so the Open2 storage-session GUID (uppercase) is accepted
directly as strHandle with no legacy pipe.

Live-verified against the real 2023 R2 server over a write-enabled (0x401) gRPC
session: AddNonStreamValuesBegin returns a real strTransactionId, and
AddNonStreamValuesEnd(bCommit=false) discards it cleanly (no data written). On
2020 WCF the same op returns UnknownClient(51) for every handle + priming chain.

- HistorianGrpcRevisionProbe + grpc-revision-probe CLI command + gated test
  NonStreamedWriteTransaction_OverGrpc_BeginsAndDiscards (live pass).
- HistorianGrpcHandshake.OpenSession gains an optional connectionMode param
  (default read-only 0x402; pass 0x401 for write-enabled) — non-breaking.
- Docs: revision-write-path.md "the wall is gone" section; roadmap M3 section,
  R3.1-R3.3 rows, one-glance table, and status note updated honestly.

Not yet shipped: the AddNonStreamValues btInput VTQ buffer is uncaptured (never
guess wire bytes), so no value-commit is implemented. Scope is non-streamed
ORIGINAL backfill; revision EDITS (R4.2) remain pipe-only even on gRPC.

272 unit tests pass; sanitization scan clean.

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:
Joseph Doherty
2026-06-21 17:51:17 -04:00
parent 04ea0b9a1f
commit 23798db1ef
6 changed files with 319 additions and 17 deletions
+61 -1
View File
@@ -1,6 +1,66 @@
# Plan: Revision-Write Path (`AddRevisionValuesBegin/Value/End`)
Status: **ARCHITECTURALLY BLOCKED verified 2026-05-05.** Same root
Status: **WCF: ARCHITECTURALLY BLOCKED (verified 2026-05-05).** **gRPC (2023 R2): the
non-streamed-original transaction is REACHABLE — Begin/End round-trip LIVE-VERIFIED 2026-06-21.**
Same root cause on WCF as `AddS2`: the `TransactionService` relay needs a pre-existing
storage-engine *pipe* session no WCF op can create. The 2023 R2 gRPC front door removes that wall
(see the §"2023 R2 gRPC — the wall is gone" section immediately below); the legacy WCF analysis is
preserved unchanged after it.
## 2023 R2 gRPC — the wall is gone (non-streamed original writes), LIVE-VERIFIED 2026-06-21
The whole D2 WCF blocker was: `ITransactionServiceContract2.AddNonStreamValuesBegin2` returns
`04 33 00 00 00` = `UnknownClient (51)` because the server-side Trx relay requires a storage-engine
pipe session (`STransactPipeClient2``aaStorageEngine.exe`) that no WCF op establishes. On the
**2023 R2 gRPC** transport that relay is replaced by a first-class `TransactionService` gRPC
service, and the gRPC server is itself the gateway to the storage engine — so the client passes the
**HistoryService Open2 storage-session GUID** straight in as `strHandle` and the transaction opens.
**Live probe (`grpc-revision-probe` CLI command / `HistorianGrpcRevisionProbe`):** against the real
2023 R2 server (History iface 12), over a **write-enabled** (`0x401`) gRPC session —
| step | result |
|---|---|
| `HistoryService.OpenConnection` (write-enabled `0x401`) | ✅ `OpenSucceeded`, client handle + storage GUID returned |
| `TransactionService.GetTransactionInterfaceVersion` | ✅ error 0, **version 2** |
| `TransactionService.AddNonStreamValuesBegin(strHandle = storage GUID **UPPERCASE**)` | ✅ **`BeginSucceeded`** — returns a real `strTransactionId` (e.g. `…-FE0A-4822-…`) on the **first** handle format tried |
| `TransactionService.AddNonStreamValuesEnd(handle, txId, bCommit=**false**)` | ✅ `EndDiscardSucceeded` — transaction discarded, **no data written** |
So the answer to the roadmap's open M3-over-gRPC question ("does the 2023 R2 gRPC front door expose
a non-streamed write that bypasses the legacy storage-engine pipe?") is **YES** — Begin/End is
reachable from the pure-managed SDK with no pipe, no native wrapper. The probe is committed as the
`grpc-revision-probe` CLI command + the gated test
`HistorianGrpcIntegrationTests.NonStreamedWriteTransaction_OverGrpc_BeginsAndDiscards`; re-run any
time to confirm the path is still open.
### Decompile basis (handle + op group)
`Archestra.Historian.GrpcClient.GrpcHistoryClient` drives the identical three-phase sequence
(`AddNonStreamValuesBegin(strHandle) → strTransactionId`; `AddNonStreamValues(strHandle,
strTransactionId, btInput)`; `AddNonStreamValuesEnd(strHandle, strTransactionId, bCommit)`), passing
the Open2 session GUID as `strHandle`. `btInput` is the **same opaque native VTQ buffer** the 2020
path uses. Proto: `src/AVEVA.Historian.Client/Grpc/Protos/TransactionService.proto`.
### What is proven vs. what remains (do NOT ship yet)
-**Proven:** the transaction lifecycle (Begin → End/rollback) is reachable over gRPC. The D2
architectural wall is specific to the WCF transport.
-**Not yet captured:** the `AddNonStreamValues` **`btInput` VTQ buffer byte layout**. Per project
discipline ("never guess wire bytes; capture first") no value-commit is implemented. The next step
to actually *ship* M3 (`AddHistoricalValuesAsync`) is to capture the native gRPC `AddNonStreamValues`
`btInput` (or decode the `GrpcHistoryClient` serializer), build a golden-tested serializer, then do a
real `bCommit=true` write + SQL read-back against a sandbox tag created by `EnsureTagAsync`.
- 🔒 **Scope:** this is **non-streamed ORIGINAL backfill** (`HistorianDataCategory.NonStreamedOriginal`
`TransactionService.AddNonStreamValues*`). **Revision EDITS** (`AddRevisionValue(s)` /
`RevisionInsert*`, the R4.2 path) are NOT on the gRPC contract even in 2023 R2 — the capability
matrix confirms they still ride the storage-engine pipe. The gRPC unlock here is original backfill,
not after-the-fact edits.
---
## Legacy WCF analysis (preserved — still accurate for the 2020 WCF transport)
Status (WCF only): **ARCHITECTURALLY BLOCKED — verified 2026-05-05.** Same root
cause as `AddS2`: client-side cache rejects values for tags that
weren't registered through a configured IO server / Application Server
pipeline. Documented below; implementation deferred until / unless that