125 lines
7.7 KiB
Markdown
125 lines
7.7 KiB
Markdown
# Original 1.5.378.182 canonical adapter — partial diagnostic proof
|
||
|
||
This folder compiles a standalone diagnostic against the **unchanged original
|
||
83ed880e published host/client DLLs**. It does not rebuild or patch product code,
|
||
restore a substitute SDK, edit a baseline oracle, install a replacement session
|
||
factory, or approve a performance denominator. Only short 1–4-cycle smoke runs are
|
||
implemented. No original warmed/full/two-hour run has been performed.
|
||
|
||
The immutable SDK reports `1.5.378.182+6cca32dd6d79c8b57d2e00e4d2071b991d2cf71b`.
|
||
The source workload was ported from `Sdk2LifecycleSoakTests` at `75057d8c`.
|
||
`evidence.json` records source/input hashes, exact SDK layout and short-run results.
|
||
Raw logs and task-owned PKI stay outside the repository; credentials are not logged.
|
||
|
||
## Supported operation coverage
|
||
|
||
`CanonicalOperations.cs` preserves the candidate's operation ordering, status
|
||
assertions, timeouts, model, grants, event data, page size and typed-array shape.
|
||
The representation changes are explicit: SDK2 Variant/ArrayOf helpers become the
|
||
original Variant constructor and CLR values; ByteString becomes byte[]; and
|
||
QualifiedName.From becomes its original constructor. There are no collection
|
||
shims or private-state mutations.
|
||
|
||
| Canonical operation per normal cycle | Original short control observation |
|
||
|---|---|
|
||
| Two encrypted opc.tcp / SignAndEncrypt sessions, Raw/UNS URI binding | Completed |
|
||
| One subscription, three data items, notifications | Completed |
|
||
| One Write service with Int32/Int16[]/UNS Int32 values, three readbacks | Completed with original Good/type assertions |
|
||
| Two browse pages, one explicit release and one abandonment | Completed; active browse count 1 |
|
||
| History first page, foreign-session denial, resume, explicit release, six abandoned points | All 10 HistoryRead services returned; active history count 6 |
|
||
| Writable off/on, array removal/recreation/publication | Completed |
|
||
| Key revocation, denied read, authority restoration | Completed |
|
||
| Paused TCP read, one-second cancellation, resume and successful recovery read | Completed; original UA status 2156134400 on the canceled read |
|
||
| Subscription deletion/removal and address-space rebuild/reconfiguration | Completed |
|
||
| Both normal clients CloseAsync | Both returned; version-specific post-close list comparison differs |
|
||
| Every-fourth-cycle restart with an active session/subscription/item, browse/history points | Separate isolated one-restart control completed |
|
||
|
||
The failed normal control attempted one whole wire body. Its ledger records
|
||
2 connects, 1 subscription create/apply/delete/remove, 1 Write service (3 values),
|
||
5 ReadValue attempts (4 returned, 1 expected canceled fault), 1 revoked Read
|
||
service, 2 Browse calls, 1 BrowseNext release, 10 HistoryRead calls, and 2 CloseAsync
|
||
returns. Surgery, revocation, stalled-network recovery and rebuild all occurred
|
||
before the post-close comparison. No normal cycle is reported as ownership-complete.
|
||
|
||
The independent active-restart control does **not** stand in for a successful
|
||
four-cycle sequence: it starts one client/subscription/item, abandons browse and
|
||
history points, stops/reboots the same URI/port/PKI server, and checks the captured
|
||
old managers/sessions/subscription items and new manager counts. Those checks
|
||
completed. It returned **exit 2**, explicitly unsupported ownership parity,
|
||
not a green acceptance result.
|
||
|
||
## Close and ownership differences — unresolved
|
||
|
||
The first normal smoke returned **exit 1** at the copied zero-closed-list
|
||
comparison: a retained server Session still had **1 browse / 6 history** points.
|
||
The server SessionManager already had **zero active sessions**. A separate fixed
|
||
250 ms read-only observation saw the same lists; the original failed comparison
|
||
was still enforced. The first failure artifacts were left untouched.
|
||
|
||
Exact immutable-binary inspection explains the internal difference:
|
||
|
||
- SessionManager.CloseSession removes the session and calls Session.Close.
|
||
- Session.Close only deletes its diagnostics; it does not clear continuation lists.
|
||
- Session.Dispose clears both lists and disposes their retained values.
|
||
- SessionManager.Shutdown/Dispose disposes sessions still in its active dictionary.
|
||
|
||
These are **version-specific diagnostics**, not proof that the original violates
|
||
the wire close contract, has an unbounded leak, or retains the same objects once
|
||
all strong diagnostic references are dropped. The adapter deliberately holds the
|
||
old Session references while checking these fields. The SDK2 zero-list invariant
|
||
cannot silently become a shared cross-version acceptance contract.
|
||
|
||
The restart control follows the different active-session shutdown path: captured
|
||
old session/point lists and subscription items became empty. The original
|
||
validator's cached store **references** remained 1 before and after stop. A cached
|
||
store reference is not a count of open handles or proof of a leak.
|
||
|
||
Original ApplicationInstance and CertificateValidator expose no Dispose API, and
|
||
there is no SDK2 CertificateManager/m_disposed/core-store-clear equivalent. The
|
||
adapter reports those observables as **unsupported**, never as zero. Actual PKI
|
||
file descriptors can be measured with the same bounded/canonical-path observer
|
||
as the candidate; those observations cannot invent the missing manager ownership
|
||
proof. No manual Session.Dispose, store replacement or cache clearing was added
|
||
to make an assertion pass.
|
||
|
||
Before any common lifecycle/acceptance protocol can be proposed, a separately
|
||
reviewed short control must establish original SessionManager removal, rejection
|
||
of the old authentication token, rejection of old CP tokens from a fresh session,
|
||
then drop every strong session/point/test reference at an isolated helper boundary
|
||
and perform bounded GC/weak-reference observation. Any retained IDisposable point
|
||
resource needs direct source/ownership inspection. Certificate-store/native
|
||
ownership gaps must remain explicit. This proposal has **not** been executed.
|
||
|
||
The standalone adapter also lacks the candidate's xUnit/VSTest host; equal process
|
||
memory overhead is not proven. Earlier original-subset managed/timer growth is an
|
||
observation, not an approved RSS denominator or a proven SDK1 defect. Resource and
|
||
operation parity require Astra/root review before any long run; no cap, baseline
|
||
or acceptance protocol changes are included here.
|
||
|
||
## Build and short controls
|
||
|
||
`build.py` selects original DLLs from the provided immutable directories (host
|
||
wins duplicate names), plus Shouldly 4.3.0/assertion dependencies only. It emits
|
||
SHA-256 provenance and a new external project. No production project/package file
|
||
is touched. The inventory lists exact types, fields, lifecycle APIs and MVIDs
|
||
without creating product instances.
|
||
|
||
```bash
|
||
python3 scripts/validation/opcua-lifecycle-memory/baseline-canonical/build.py \
|
||
--host "$originalHost" --client "$originalClient" --assertions "$assertionDlls" \
|
||
--output /tmp/original-canonical-build
|
||
python3 scripts/validation/opcua-lifecycle-memory/baseline-canonical/run_smoke.py \
|
||
--adapter /tmp/original-canonical-build/bin/Release/net10.0/adapter.dll \
|
||
--output /tmp/original-canonical-close --mode normal
|
||
python3 scripts/validation/opcua-lifecycle-memory/baseline-canonical/run_smoke.py \
|
||
--adapter /tmp/original-canonical-build/bin/Release/net10.0/adapter.dll \
|
||
--output /tmp/original-canonical-active-restart --mode active-restart
|
||
```
|
||
|
||
Add `--inventory` to build the read-only metadata inventory instead. The runner
|
||
uses a fresh private directory and a temporary environment credential with the
|
||
same prefix/byte length as the canonical fixture; it preserves the original child
|
||
exit. Exit 1 is an observed control failure; exit 2 is operations completed with
|
||
unsupported ownership parity. Neither is a passing baseline. The smoke driver
|
||
has a hard 120-second bound and no long-run option.
|