Skip to content

Promote V2 SubscriptionManager to ISession.TryGetSubscriptionManager (#3925)#3933

Merged
marcschier merged 5 commits into
masterfrom
part14pubsubfollowup
Jul 2, 2026
Merged

Promote V2 SubscriptionManager to ISession.TryGetSubscriptionManager (#3925)#3933
marcschier merged 5 commits into
masterfrom
part14pubsubfollowup

Conversation

@marcschier

@marcschier marcschier commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to the merged PR #3892 implementing issue #3925.

Summary

Promotes the V2 options-based subscription manager onto ISession so the PubSub external-server adapter no longer downcasts to the concrete ManagedSession.

The V2 ISubscriptionManager is backed by the session's subscription engine, so it is a Session capability rather than a managed-only one. It is now exposed on ISession via a non-throwing accessor:

bool TryGetSubscriptionManager([NotNullWhen(true)] out ISubscriptionManager? manager)

Returns true + the manager for V2-engine sessions (the default for ManagedSession) and false for classic-engine sessions (the default for the unmanaged Session).

Changes

  • ISession: add TryGetSubscriptionManager.
  • Session (unmanaged): implement off the active subscription engine.
  • ManagedSession: implement by delegating to the inner session. The throwing ManagedSession.SubscriptionManager property is removed (source-breaking, 2.0 preview) in favour of the ISession accessor.
  • Callers migrated: public ManagedSessionExtensions helpers (signatures unchanged, via a private resolver), ManagedSession.Streaming, and the client subscription/session/stress test suites (via a RequireSubscriptionManager test helper).
  • PubSub adapter (PubSub Adapter: promote SubscriptionManager/MessageContext to ISession and drop ManagedSession casts #3925): ServerSession reaches the subscription manager through TryGetSubscriptionManager and MessageContext through ISession directly (it was already on ISession via IClientBase). All three (ManagedSession) downcasts and the CA1859 suppression are removed; CreateSessionAsync returns ISession. An internal session-injecting constructor was added as a test seam for the classic-engine capability guards.
  • Docs: Sessions.md / Subscriptions.md updated; migrate/2.0.x/sessions-subscriptions.md documents the property removal.

Review feedback addressed

  • Actionable BadNotSupported message (names the V2 engine + endpoint); classic-engine model-change monitoring log clarified as optional/best-effort.
  • GetSubscriptionManager argument null-check.
  • Tests assert the concrete SubscriptionManager type before downcasting.

CI fixes

  • Replaced ArgumentNullException.ThrowIfNull (unavailable on net472/net48/netstandard2.0) with the classic throw form in the test helper.
  • Added tests covering the new classic-engine guard branches (AddSubscription/DefaultStreaming throws; ServerSession data-change throw + model-change no-op) to keep PR diff coverage above target.

Validation

  • Opc.Ua.Client + Opc.Ua.PubSub.Adapter build 0-warning on net10 and net48.
  • Opc.Ua.Subscriptions.Tests and Opc.Ua.PubSub.Adapter.Tests pass; no (ManagedSession) casts remain in product code.

Closes #3925

…3925)

The V2 options-based subscription manager is backed by the session's
subscription engine, so it is a Session capability rather than a
managed-only one. Expose it on ISession via a non-throwing
  bool TryGetSubscriptionManager(out ISubscriptionManager? manager)
returning true + the manager for V2-engine sessions and false for
classic-engine sessions (the unmanaged Session defaults to classic).

- ISession: add TryGetSubscriptionManager.
- Session (unmanaged): implement off the active subscription engine.
- ManagedSession: implement by delegating to the inner session; the
  throwing ManagedSession.SubscriptionManager property is removed.
- Migrate all client-side callers of the removed property to
  TryGetSubscriptionManager: public ManagedSessionExtensions helpers
  (via a private GetSubscriptionManager resolver, signatures
  unchanged), ManagedSession.Streaming, and the client
  subscription/session/stress test suites (via a RequireSubscriptionManager
  test helper).

PubSub adapter: ServerSession reaches SubscriptionManager through
TryGetSubscriptionManager and MessageContext through ISession directly
(MessageContext was already on ISession via IClientBase), so all three
(ManagedSession) downcasts and the CA1859 suppression are removed;
CreateSessionAsync returns ISession.

Docs: Sessions.md / Subscriptions.md updated; migrate/2.0.x/
sessions-subscriptions.md documents the property removal.

Closes #3925
Copilot AI review requested due to automatic review settings July 1, 2026 07:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.

Promotes the V2 options-based subscription manager capability to ISession via TryGetSubscriptionManager, removing ManagedSession.SubscriptionManager and updating product code, tests, and docs to avoid downcasts to ManagedSession.

Changes:

  • Added ISession.TryGetSubscriptionManager(out ISubscriptionManager?) and implemented it in Session and ManagedSession.
  • Updated PubSub adapter and client helpers to resolve the V2 manager via TryGetSubscriptionManager instead of (ManagedSession) casts / concrete access.
  • Migrated tests and docs to use TryGetSubscriptionManager (and a test-only RequireSubscriptionManager() helper).

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
Tests/Opc.Ua.Subscriptions.Tests/TransferSubscriptionTests.cs Migrates V2 subscription persistence calls to RequireSubscriptionManager() helper.
Tests/Opc.Ua.Subscriptions.Tests/SubscriptionTests.cs Updates V2 assertions and save/load to use RequireSubscriptionManager().
Tests/Opc.Ua.Subscriptions.Tests/SubscriptionEngineIntegrationTests.cs Updates integration tests to use RequireSubscriptionManager() for V2-only behavior.
Tests/Opc.Ua.Subscriptions.Tests/SubscriptionCoverageTests.cs Switches V2 restore tests to RequireSubscriptionManager() (still casts to concrete type).
Tests/Opc.Ua.Subscriptions.Tests/ManagedSessionSubscriptionManagerIntegrationTests.cs Validates TryGetSubscriptionManager behavior for V2 vs classic engine sessions.
Tests/Opc.Ua.Stress.Tests/Channels/Chaos/SubscriptionSurvivalChaosTests.cs Uses RequireSubscriptionManager() in stress readiness checks.
Tests/Opc.Ua.Sessions.Tests/ManagedSessionReconnectIntegrationTests.cs Updates concrete manager access to flow through RequireSubscriptionManager().
Tests/Opc.Ua.Client.TestFramework/Extensions.cs Adds RequireSubscriptionManager() test helper that throws when V2 manager isn’t available.
Libraries/Opc.Ua.PubSub.Adapter/Session/ServerSession.cs Removes ManagedSession downcasts; uses TryGetSubscriptionManager; CreateSessionAsync now returns ISession.
Libraries/Opc.Ua.Client/Session/Session.cs Implements ISession.TryGetSubscriptionManager by checking the active subscription engine.
Libraries/Opc.Ua.Client/Session/ManagedSessionOptions.cs Updates docs/comments to reference ISession.TryGetSubscriptionManager.
Libraries/Opc.Ua.Client/Session/ManagedSession.cs Removes throwing SubscriptionManager property; delegates TryGetSubscriptionManager to inner ISession.
Libraries/Opc.Ua.Client/Session/ManagedSession.Streaming.cs Resolves the V2 manager via TryGetSubscriptionManager and throws if classic engine is used.
Libraries/Opc.Ua.Client/Session/ISession.cs Adds the TryGetSubscriptionManager API contract and documentation.
Libraries/Opc.Ua.Client/Fluent/ManagedSessionExtensions.cs Switches fluent helpers to resolve the manager via a private resolver using TryGetSubscriptionManager.
Docs/migrate/2.0.x/sessions-subscriptions.md Documents the breaking removal of ManagedSession.SubscriptionManager and the new ISession API.
Docs/Subscriptions.md Updates docs to use TryGetSubscriptionManager instead of ManagedSession.SubscriptionManager.
Docs/Sessions.md Updates session/subscription-engine docs to reference TryGetSubscriptionManager.

Comment thread Libraries/Opc.Ua.PubSub.Adapter/Session/ServerSession.cs
Comment thread Libraries/Opc.Ua.PubSub.Adapter/Session/ServerSession.cs
Comment thread Tests/Opc.Ua.Subscriptions.Tests/SubscriptionCoverageTests.cs Outdated
Comment thread Libraries/Opc.Ua.Client/Fluent/ManagedSessionExtensions.cs
- ServerSession.CreateDataChangeSubscriptionAsync: expand the BadNotSupported
  message to name the V2-engine requirement (DefaultSubscriptionEngineFactory)
  and include the endpoint URL for troubleshooting.
- ServerSession.StartModelChangeMonitoringAsync: clarify the classic-engine
  log message (optional/best-effort monitoring, V2-engine guidance, endpoint
  context); the no-op remains intentional since model-change monitoring is
  best-effort (unlike required data-change subscriptions which throw).
- ManagedSessionExtensions.GetSubscriptionManager: add a null-check on the
  session argument, matching the file's public helpers.
- SubscriptionCoverageTests: assert the manager is a concrete SubscriptionManager
  (Is.InstanceOf) before downcasting, so a type mismatch fails diagnosably
  rather than as an unexpected InvalidCastException.
@codecov

codecov Bot commented Jul 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.46154% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.52%. Comparing base (f9a8343) to head (77fc9a3).

Files with missing lines Patch % Lines
...s/Opc.Ua.Client/Fluent/ManagedSessionExtensions.cs 85.71% 1 Missing and 1 partial ⚠️
.../Opc.Ua.Client/Session/ManagedSession.Streaming.cs 71.42% 1 Missing and 1 partial ⚠️
...ies/Opc.Ua.PubSub.Adapter/Session/ServerSession.cs 92.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #3933   +/-   ##
=======================================
  Coverage   73.52%   73.52%           
=======================================
  Files        1169     1169           
  Lines      169907   169942   +35     
  Branches    29296    29302    +6     
=======================================
+ Hits       124927   124954   +27     
+ Misses      34005    34004    -1     
- Partials    10975    10984    +9     
Files with missing lines Coverage Δ
Libraries/Opc.Ua.Client/Session/ManagedSession.cs 63.71% <100.00%> (-0.27%) ⬇️
...ies/Opc.Ua.Client/Session/ManagedSessionOptions.cs 100.00% <ø> (ø)
Libraries/Opc.Ua.Client/Session/Session.cs 73.54% <100.00%> (+0.05%) ⬆️
...s/Opc.Ua.Client/Fluent/ManagedSessionExtensions.cs 73.11% <85.71%> (+0.23%) ⬆️
.../Opc.Ua.Client/Session/ManagedSession.Streaming.cs 24.59% <71.42%> (+8.22%) ⬆️
...ies/Opc.Ua.PubSub.Adapter/Session/ServerSession.cs 68.15% <92.00%> (+1.58%) ⬆️

... and 11 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…Null in test helper

ArgumentNullException.ThrowIfNull is unavailable on net472/net48/netstandard2.0,
which broke build-windows-all-tfm. Use the classic throw form in the
Opc.Ua.Client.TestFramework RequireSubscriptionManager helper, matching the
same fix already applied to ManagedSessionExtensions.
Extend ClassicEngineDoesNotExposeSubscriptionManager to assert that the V2-only
fluent surfaces (AddSubscription, DefaultStreaming) throw InvalidOperationException
on a classic-engine ManagedSession, exercising the new guard branches in
ManagedSessionExtensions.GetSubscriptionManager and ManagedSession.DefaultStreaming.
Add an internal session-injecting ServerSession constructor (testability seam)
and ServerSessionCapabilityGuardTests, which bind a Mock<ISession> whose
TryGetSubscriptionManager returns false to exercise the classic-engine guards:
CreateDataChangeSubscriptionAsync throws BadNotSupported and
StartModelChangeMonitoringAsync no-ops. These branches are unreachable through
the adapter's normal V2-session path, so the seam is required to cover them.
@marcschier
marcschier merged commit a6fa824 into master Jul 2, 2026
175 checks passed
@marcschier
marcschier deleted the part14pubsubfollowup branch July 2, 2026 05:40
marcschier added a commit to marcschier/UA-.NETStandard that referenced this pull request Jul 9, 2026
Brings in 2 upstream commits: OPCFoundation#3933 (promote V2 SubscriptionManager to
ISession.TryGetSubscriptionManager) and OPCFoundation#3932 (honor
SecurityConfiguration in GDS push UpdateCertificate validation).

No textual merge conflicts, but OPCFoundation#3933 replaced the
ManagedSession.SubscriptionManager property (which threw
InvalidOperationException on the classic engine) with a
bool ISession.TryGetSubscriptionManager(out ISubscriptionManager?)
try-get. Adapted the 9 UaLens call sites:

 * ChannelV2EngineAdapter.cs — the six worker-pool / republish counter
   getters (PublishWorkerCount, BadPublishRequestCount,
   MissingMessageCount, RepublishMessageCount, MinPublishWorkerCount,
   MaxPublishWorkerCount) now use the try-get ternary
   (`TryGetSubscriptionManager(out var mgr) ? mgr.X : 0`) instead of
   try/catch(InvalidOperationException); the subscription-create path
   throws an explicit InvalidOperationException when the V2 manager is
   unavailable, preserving prior behavior.
 * SubscriptionBenchPlugin.cs — the bench's Add path now checks
   TryGetSubscriptionManager up front (V2-engine-required guard) before
   calling Add; the engine-metrics panel switched its
   try/catch(InvalidOperationException) fallback to a plain if/else on
   the try-get result.

UaLens build clean (0 warnings / 0 errors).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PubSub Adapter: promote SubscriptionManager/MessageContext to ISession and drop ManagedSession casts

3 participants