Files
lmxopcua/code-reviews/Driver.OpcUaClient.Browser/findings.md
T
Joseph Doherty 2fe8e587dd review(Driver.OpcUaClient.Browser): AttributesAsync updates LastUsedUtc
Review at HEAD 7286d320. -001: AttributesAsync now updates LastUsedUtc (IBrowseSession
contract) + test (InternalsVisibleTo+Moq added). -002 (continuation-point cancel leak)
deferred cross-cutting w/ runtime Driver.OpcUaClient.
2026-06-19 10:52:23 -04:00

4.6 KiB

Code Review — Driver.OpcUaClient.Browser

Field Value
Module src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser
Reviewer Claude Code
Review date 2026-06-19
Commit reviewed 7286d320
Status Reviewed
Open findings 0

Checklist coverage

A comprehensive review completes every category, recording "No issues found" where a category produced nothing rather than leaving it blank.

# Category Result
1 Correctness & logic bugs Finding 001 (LastUsedUtc not updated in AttributesAsync)
2 OtOpcUa conventions No issues found
3 Concurrency & thread safety No issues found (disposal race is accepted pattern, same as GalaxyBrowseSession; TTL reaper evicts only idle sessions)
4 Error handling & resilience No issues found (BrowseRoot parse failure is caught and session cleaned up; continuation-point cancel-leak is cross-cutting — see 002)
5 Security No issues found (separate PKI store correct; AutoAcceptCertificates not forwarded to browse store; no credential logging)
6 Performance & resource management Finding 002 (continuation-point server-side leak on cancellation)
7 Design-document adherence No issues found
8 Code organization & conventions No issues found
9 Testing coverage No issues found (unit tests cover pre-connect validation; live tests correctly gated on RequiresOpcPlc)
10 Documentation & comments No issues found

Findings

Driver.OpcUaClient.Browser-001

Field Value
Severity Low
Category Correctness & logic bugs
Location src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/OpcUaClientBrowseSession.cs:66
Status Resolved

Description: AttributesAsync returns Task.FromResult(Array.Empty<AttributeInfo>()) without updating LastUsedUtc. The IBrowseSession interface contract (at src/Core/ZB.MOM.WW.OtOpcUa.Commons/Browsing/IBrowseSession.cs:14-15) states that LastUsedUtc is "Refreshed on RootAsync, ExpandAsync, and AttributesAsync". If the AdminUI calls AttributesAsync exclusively for 2+ minutes without an intermediate RootAsync or ExpandAsync (e.g. in a polymorphic picker context), the BrowseSessionReaper may evict an active session early.

Although the OpcUaClient browser never issues a server round-trip in AttributesAsync (it always returns empty), the timestamp must be refreshed to honour the contract and keep the session alive while the user is active.

Recommendation: Set LastUsedUtc = DateTime.UtcNow inside AttributesAsync before returning, mirroring the pattern in BrowseOneLevelAsync.

Resolution: Fixed 2026-06-19 (SHA pending commit) — added LastUsedUtc = DateTime.UtcNow to AttributesAsync body; regression test AttributesAsync_updates_LastUsedUtc added to the unit test project.


Driver.OpcUaClient.Browser-002

Field Value
Severity Low
Category Performance & resource management
Location src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser/OpcUaClientBrowseSession.cs:112-125
Status Deferred

Description: If the caller's CancellationToken fires during the BrowseNextAsync pagination loop, OperationCanceledException propagates out of the loop and any in-progress server-side continuation point is never released via BrowseNext(releaseContinuationPoints: true). The server holds the cursor until the session is closed. For the transient AdminUI picker session this is low-risk (the session is short-lived and DisposeAsyncCloseAsync eventually cleans it up server-side), but it represents a server-side resource leak during cancellation.

The identical pattern exists in the runtime driver (OpcUaClientDriver.cs), making this a cross-cutting concern rather than a module-local defect.

Recommendation: Catch OperationCanceledException in the pagination loop, issue a fire-and-forget BrowseNext(releaseContinuationPoints: true, continuationPoints: [cp]) before re-throwing, and apply the same fix to Driver.OpcUaClient.

Resolution: Deferred — awaiting a cross-cutting fix that also updates Driver.OpcUaClient. Short session lifetime of the AdminUI picker limits practical impact.