D2 (revision-write): empirically blocked by same gate as AddS2
Drove the revision-write flow via reflection in the native trace harness (--write-revision-values) to see whether it bypasses the AddS2 architectural blocker. It doesn't. Findings: - HistorianAccess.CreateHistorianDataValueList(NonStreamedOriginal) succeeds - HistorianDataValueList.NonStreamedValuesBegin() succeeds (batchID 0->1) - HistorianDataValueList.AddNonStreamedValue(value, validate=true, out err) FAILS with ErrorCode=TagNotFoundInCache (129) — same client-side validation gate that blocks AddS2 - AddNonStreamedValuesEnd() returns void; SendValues() returns true with Success because the list is empty (no value was ever added) - No AddNonStreamValues* WCF calls reach the wire Conclusion: the revision-write path requires the tag to be in the library's runtime tag cache, which is only populated by configured IO server / Application Server pipelines, not by HistorianAccess.AddTag. This matches the architectural blocker documented for AddS2 and means no public WriteRevisionsAsync / BeginRevisionAsync should be added to the SDK — the path is unreachable for client-created sandbox tags. The Wcf/Contracts/ITransactionServiceContract methods (AddNonStream- ValuesBegin/AddNonStreamValues/AddNonStreamValuesEnd) remain declared for completeness; no orchestrator or public surface is added. The harness extension is preserved as a deterministic reproducer for the blocker: re-run --write-revision-values to verify the gate any time. docs/plans/revision-write-path.md updated with the empirical finding plus the original plan retained as historical context. 177/177 tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,94 @@
|
||||
# Plan: Revision-Write Path (`AddRevisionValuesBegin/Value/End`)
|
||||
|
||||
Status: **NOT STARTED.** Sub-plan extracted from `speculative-items-sweep.md`
|
||||
item D2 because the work is too large for a one-push sweep.
|
||||
Status: **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
|
||||
prerequisite is removed.
|
||||
|
||||
## Empirical finding (2026-05-05)
|
||||
|
||||
The native trace harness was extended with `--write-revision-values` to
|
||||
drive the revision flow:
|
||||
|
||||
1. `HistorianAccess.CreateHistorianDataValueList(HistorianDataCategory.NonStreamedOriginal)`
|
||||
succeeds — list is bound to the live `HistorianClient*` via
|
||||
`GetClient(ConnectionIndex.Process)`.
|
||||
2. `HistorianDataValueList.NonStreamedValuesBegin()` succeeds — list
|
||||
batchID transitions 0 → 1.
|
||||
3. `HistorianDataValueList.AddNonStreamedValue(value, validate=true, out error)`
|
||||
**fails** with `ErrorCode=TagNotFoundInCache (129)`,
|
||||
`ErrorDescription="error = 129 (Tag not found in cache)"` — the value
|
||||
is never added to the list (`Count` stays 0).
|
||||
4. `HistorianDataValueList.AddNonStreamedValuesEnd()` returns void.
|
||||
5. `HistorianAccess.SendValues(list, out error)` returns `true` with
|
||||
`ErrorCode=Success` — **but** no wire bytes left the client because
|
||||
the list is empty. (Inspecting captured WriteMessage stream confirms
|
||||
no `AddNonStreamValues*` Trx call appears.)
|
||||
|
||||
The validation that rejects the value is the same gate that blocks
|
||||
`AddStreamedValue` (`AddS2`): the library's local tag cache only knows
|
||||
about tags that were:
|
||||
|
||||
- Auto-populated from a configured IO server / Application Server pipeline, or
|
||||
- Read via the existing read flow (which hits the cache as a side effect)
|
||||
|
||||
Tags created via `HistorianAccess.AddTag` populate `Runtime.dbo.Tag` but
|
||||
are not added to the in-memory cache that AddStreamedValue /
|
||||
AddNonStreamedValue consult. So writes from a managed client to a
|
||||
client-created tag fail at the validation gate before any wire bytes
|
||||
flow.
|
||||
|
||||
## Conclusion
|
||||
|
||||
The revision-write path **does not bypass the AddS2 blocker** — it
|
||||
shares the same `TagNotFoundInCache` precondition. There is no path
|
||||
from a managed client to a successful AddNonStreamValues call against
|
||||
a client-created sandbox tag.
|
||||
|
||||
To validate this conclusion further (not done in this pass — too risky
|
||||
for the production Historian) one could try AddNonStreamedValue against
|
||||
a system tag like `SysTimeSec` whose key IS in the cache from upstream
|
||||
registration. If that succeeds, the path is implementable in principle
|
||||
for IO-registered tags; if it also fails, the prerequisite is even
|
||||
stricter.
|
||||
|
||||
## Decision
|
||||
|
||||
Do **not** add public `WriteRevisionsAsync` / `BeginRevisionAsync` to
|
||||
the SDK. The contract methods already exist in
|
||||
`Wcf/Contracts/ITransactionServiceContract.cs`
|
||||
(`AddNonStreamValuesBegin/AddNonStreamValues/AddNonStreamValuesEnd`)
|
||||
for completeness, but the orchestrator and public surface stay absent.
|
||||
|
||||
Revisit if either of these changes:
|
||||
|
||||
1. AVEVA documents (or a customer demonstrates) a code path that
|
||||
bypasses the cache validation for client-created tags.
|
||||
2. The SDK's mission expands to include data correction for tags that
|
||||
ARE in the runtime cache (i.e., tags managed by a real IO server),
|
||||
in which case the harness extension below provides a starting point.
|
||||
|
||||
## Harness diagnostic (preserved)
|
||||
|
||||
The `--write-revision-values` flag in
|
||||
`tools/AVEVA.Historian.NativeTraceHarness/Program.cs` reproduces the
|
||||
above failure deterministically. Re-run it any time to verify the
|
||||
blocker still holds:
|
||||
|
||||
```powershell
|
||||
dotnet run --no-build --project tools\AVEVA.Historian.NativeTraceHarness -- `
|
||||
--scenario write `
|
||||
--write-sandbox-tag RetestSdkWriteRevSandbox `
|
||||
--write-data-type Float `
|
||||
--write-skip-add-tag --write-skip-add-value `
|
||||
--write-revision-values
|
||||
```
|
||||
|
||||
Look for the `AddNonStreamedValue` row's `ErrorCode` field in the JSON
|
||||
output.
|
||||
|
||||
## Original plan (preserved for context if the blocker ever lifts)
|
||||
|
||||
## Context
|
||||
|
||||
|
||||
Reference in New Issue
Block a user