D2: definitive conclusion — revision-write requires non-WCF storage-engine pipe

IL walk of the native wrapper:

  HistorianAccess.AddRevisionValuesBegin (private, token 0x06006175)
    -> CClientCommon.AddNonStreamValuesBegin
       -> CClient.AddNonStreamValuesBegin (8-instr overload)
          -> CClient.TransactionBegin
             -> CHistStorageConnection.StartTransaction (token 0x06001FDD)
                -> CStorageEngineConsoleClient.StartTransaction

CStorageEngineConsoleClient is built on STransactPipeClient2 +
SCrtMemFile — a shared-memory + named-pipe transport to
aaStorageEngine.exe, completely separate from WCF.

The WCF ITransactionServiceContract2.AddNonStreamValuesBegin2 op is a
server-side relay that requires a pre-existing storage-engine pipe
session for the client. Without that pipe session, the WCF relay returns
UnknownClient (51) — and there's no way to establish the pipe session
via WCF.

D2 is unimplementable as a pure-managed-WCF SDK. The native wrapper
itself depends on the C++ shared-memory channel; replicating that from
managed code would require implementing the storage-engine pipe
protocol, which is a major undertaking and out of scope.

The ITransactionServiceContract2 declaration in our contracts file
stays as documentation; no public API or orchestrator added.
HistorianWcfRevisionOrchestrator remains as an internal probe /
regression check — re-run the probe test if anyone believes the
architecture has changed.

178/178 tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-05 02:59:29 -04:00
parent 6b385441c1
commit 8a553423ed
+42 -10
View File
@@ -175,16 +175,48 @@ A future session that wants to push further should try (in order):
chain — confirmed `RTag2` itself succeeds (returns 25-byte response),
but `AddNonStreamValuesBegin2` still fails with `UnknownClient`.
So RTag2 doesn't cascade client identity to Trx.
2. Try `IStorageServiceContract` ops (`AddT`, `AddTP`) on `/Storage`
— that endpoint isn't currently bound by our SDK but the contract
is declared in `Wcf/Contracts/IStorageServiceContract.cs`. Maybe
one of its ops registers the client with Trx as a side effect.
3. Decompile / IL-walk `aahClientCommon.CClientCommon` methods that
the native code calls between Open2 and AddNonStreamValuesBegin
to find any "client-with-Trx" registration we're missing.
4. As a last resort, decompile `aahClientAccessPoint.exe` (the server
binary) to find what populates Trx's session table — the answer
is in there, just not in the client surface.
2. ⚠️ **OBVIATED 2026-05-05** by finding (3): `IStorageServiceContract`
ops aren't the missing piece either, because the missing piece isn't
on the WCF surface at all.
3.**DONE 2026-05-05** — IL walk of `aahClientCommon.CClientCommon.AddNonStreamValuesBegin`
`aahClientCommon.CClient.AddNonStreamValuesBegin`
`aahClientCommon.CClient.TransactionBegin`
reveals the chain ultimately invokes
**`aahClientCommon.CHistStorageConnection.StartTransaction`** (token
`0x06001FDD`) which calls **`CStorageEngineConsoleClient.StartTransaction`**.
`CStorageEngineConsoleClient` is built on `STransactPipeClient2` +
`SCrtMemFile` — a **shared-memory + named-pipe** transport to the
storage engine, completely separate from WCF.
### Definitive architectural conclusion (2026-05-05)
The revision-write path uses **two transports in tandem**:
1. WCF (`/Hist`, `/Retr`, `/Stat`, `/Trx`) — what our SDK speaks
2. **Shared-memory + named-pipe to `aaStorageEngine.exe`** — what
`CStorageEngineConsoleClient` speaks; the SDK doesn't (and would be
a major project to implement)
The WCF `ITransactionServiceContract2.AddNonStreamValuesBegin2` op we
were probing is a server-side relay that requires a pre-existing
storage-engine pipe session for the client. That session is established
via the pipe channel, not WCF. Without the pipe-side session, the WCF
relay returns `UnknownClient (51)` — and there's no way to establish
the pipe-side session via WCF.
**D2 is unimplementable as a pure-managed-WCF SDK.** The native wrapper
itself depends on the C++ shared-memory channel; to replicate that
behavior from a managed client would require implementing the whole
storage-engine pipe protocol, which is out of scope and probably
not viable without deeper RE of `aaStorageEngine.exe` itself.
The WCF `ITransactionServiceContract2` declaration in our contracts
file is left in place — it's correct as a contract — but no
orchestrator or public surface should be added on top of it. The
`HistorianWcfRevisionOrchestrator` in `src/AVEVA.Historian.Client/Wcf/`
remains as an internal probe / regression check; if anyone ever
believes the architecture has changed, re-run the probe test to
verify the gate still holds.
### Current state of the SDK-direct probe