Files
lmxopcua/scripts/validation/opcua-lifecycle-memory/baseline-canonical/README.md
T

8.1 KiB
Raw Blame History

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.

The historical partial proof above did not test token/continuation rejection or collection after dropping its diagnostic references. Later, separately reviewed short controls established those semantics and observed the remaining server graph retention after normal shutdown. Their direct point-resource inspection, two weak-reference phases and separate terminal-disposal attribution are recorded in OWNERSHIP.md and semantic-evidence.json. Both ordinary failure results remain intact. Certificate-store/native ownership gaps remain explicit; these findings do not approve a common lifecycle/acceptance protocol.

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.

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. Normal/restart modes have a hard 120-second bound and no long-run option.

The later semantic modes are --mode semantic-retention and the separately gated --mode semantic-terminal-dispose attribution control. They use the 120-second operation deadline plus bounded independent cleanup and a 180-second outer runner limit. Neither changes normal/active-restart results or authorizes a long run.