mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-29 07:07:24 +08:00
master
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
cd88bfac23 |
Server: decouple reverse connect connection admission from global sessions (#4863) (#4889)
Hey everyone! 👋 Just wanted to contribute! I learned about the OPC Foundation during my last internship and got introduced to open source coding, so here we are! I noticed issue #4863 while exploring the codebase and wanted to help address it. --- # Description ## Problem In `ReverseConnectServer.OnReverseConnect` (`src/Opc.Ua.Server/Server/ReverseConnectServer.cs`), reverse connection admission was evaluated using a combination of `MaxSessionCount` and `ServerInternal.SessionManager.GetSessions().Count`: ```csharp if (reverseConnection.Enabled && ( reverseConnection.MaxSessionCount == 0 || ( reverseConnection.MaxSessionCount == 1 && reverseConnection.LastState == ReverseConnectState.Closed ) || reverseConnection.MaxSessionCount > ServerInternal.SessionManager .GetSessions() .Count)) ``` As reported in #4863, this logic causes several issues: 1. **Redundant connection attempts:** When `MaxSessionCount == 0` (the default if omitted or unconfigured), `MaxSessionCount == 0` is always true. Every 15-second timer tick dispatches another `CreateConnection` call, even if the connection is already `Connected` or `Connecting`. 2. **Unrelated server sessions block reverse connect:** When `MaxSessionCount >= 2`, `ServerInternal.SessionManager.GetSessions().Count` queries the global session count across all endpoints (including local/direct OPC UA clients). Once direct sessions reach `MaxSessionCount`, reverse connections are permanently prevented from establishing even when in state `Closed`. 3. **Premature connections under `MaxSessionCount == 1`:** If a reverse connection is established (`Connected`) but the client has not yet activated an OPC UA session (`GetSessions().Count == 0`), `1 > 0` evaluates to true, triggering duplicate connection attempts. 4. **Stuck `Errored` state:** If `CreateConnection` throws an exception, `LastState` is set to `Errored`. If other sessions exist on the server, `1 > 1` is false, leaving the connection permanently stuck in `Errored` without retrying. --- ## Code Explanation Following maintainer guidance on #4863 to remove session-count coupling from connection admission: In `src/Opc.Ua.Server/Server/ReverseConnectServer.cs`: ```csharp // try the reverse connect if (reverseConnection.Enabled && (reverseConnection.LastState == ReverseConnectState.Closed || reverseConnection.LastState == ReverseConnectState.Errored)) ``` - **Connection state drives admission:** A reverse connection is only initiated if it is enabled and currently `Closed` (needs connection) or `Errored` (retrying after a previous failure). - **Prevents duplicate sockets:** When already `Connected` or `Connecting`, no redundant connection attempts are dispatched. - **Respects reject backoff:** When in state `Rejected`, the connection remains idle until `m_rejectTimeout` elapses and recharges the state to `Closed`. - **Decoupled from global sessions:** Unrelated client sessions on the server no longer interfere with reverse connection establishment. - **Zero API breakage:** Kept changes minimal and surgical—no public signatures were changed, ensuring full backward compatibility. --- ## Commits Breakdown The changes were structured into two focused commits: 1. **`31fc2fd95` — `test(server): add regression tests for reverse connect session accounting (#4863)`** - Adds unit regression tests in `tests/Opc.Ua.Server.Tests/ReverseConnectServerSessionAccountingTests.cs` covering all edge cases (`MaxSessionCount == 0`, `MaxSessionCount == 1`, `MaxSessionCount >= 2`, `Errored` retry, reject timeout expiry, and disabled connections) using `FakeTimeProvider`. 2. **`b820dbfed` — `fix(server): decouple reverse connect connection admission from global sessions (#4863)`** - Applies the surgical fix to `ReverseConnectServer.OnReverseConnect` to evaluate `Closed` and `Errored` states directly. --- ## Testing & Verification - **New regression suite:** All 7 tests in `ReverseConnectServerSessionAccountingTests` pass on `net10.0` (98 ms). - **Existing suite:** All 43 tests in category `ReverseConnect` pass on `net10.0` (12 s). - **Analyzers:** Zero compiler warnings and zero Roslynator analyzer warnings (`TreatWarningsAsErrors=true`). --- ## Related Issues Fixes #4863 --- ## Checklist - [] I have signed the [CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf) and read the [CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md) doc. - [x] I have added tests that prove my fix is effective. - [x] I have verified that my changes do not introduce (new) build or analyzer warnings. - [x] I ran the affected tests locally against .NET 10 and all passed. - [] I have addressed all PR feedback received. --------- Co-authored-by: Marc Schier <marcschier@users.noreply.github.com> Co-authored-by: Marc <marcschier@hotmail.com> |
||
|
|
fac30c45e6 |
Add MinSupportedSamplingInterval configuration and honor it when revising sampling intervals (#4243)
# Description PR #4194 removed the unused `ServerConfiguration.MinMetadataSamplingInterval` and left nothing in its place. As a result there was no way to populate `Server.ServerCapabilities.MinSupportedSampleRate` (`i=2272`) — `ServerInternalData.CreateServerObjectAsync` hardcoded it to `0`. Separately, the only clamp applied when revising a monitored item's sampling interval was the per-node `BaseVariableState.MinimumSamplingInterval`, which defaults to `MinimumSamplingIntervals.Continuous` (`0`), so reference-server nodes accepted any requested interval (e.g. 10 ms) unrevised — exactly what was reported. This adds a configuration entry **in the exact schema slot #4194 vacated** and enforces it as a lower bound. ## Configuration - **`ServerConfiguration.MinSupportedSamplingInterval`** — `double` (milliseconds), default `0`, `[DataTypeField(Order = 17)]`, i.e. between `MaxNotificationsPerPublish` (16) and `AvailableSamplingRates` (18). - `ApplicationConfiguration.xsd` — `<xs:element name="MinSupportedSamplingInterval" type="xs:double" minOccurs="0" />` in the same position the removed element occupied. - Fluent API **`SetMinSupportedSamplingInterval(double)`** on `IApplicationConfigurationBuilderServerOptions`, in the place `SetMinMetadataSamplingInterval` was removed from. - The element is added to the six config XML files #4194 touched (reference / CTT / AOT / GDS use `0`; `test-server-full.xml` uses a distinctive non-zero value for the round-trip test). The address-space Property keeps its OPC UA spec name, so `Server.ServerCapabilities.MinSupportedSampleRate` and `VariableIds.Server_ServerCapabilities_MinSupportedSampleRate` are unchanged. ```xml <MaxNotificationsPerPublish>1000</MaxNotificationsPerPublish> <MinSupportedSamplingInterval>2000</MinSupportedSamplingInterval> <AvailableSamplingRates> ``` ```csharp application.Build(applicationUri, productUri) .AsServer([endpointUrl]) .SetMinSupportedSamplingInterval(2000); ``` ## Behavior - `ServerInternalData` now publishes the configured value to `Server.ServerCapabilities.MinSupportedSampleRate`. - New `SubscriptionManager.CalculateRevisedSamplingInterval(...)` (two overloads, alongside the existing `CalculateRevisedQueueSize`) centralizes the revision rule and replaces the logic that was duplicated across four call sites — the create and modify paths of `AsyncCustomNodeManager` and `CustomNodeManager` — which now expose a `MinSupportedSamplingInterval` property seeded from the configuration (mirroring how `MaxQueueSize` is initialized). The rule: 1. A requested interval below zero resolves to the *default sampling interval*: the **publishing interval of the subscription** on create, and the item's **current sampling interval** on modify. The modify case preserves 1.5.378 behavior, so a `ModifyMonitoredItems` call that leaves the sampling interval unspecified does not silently retune the item. 2. A node declaring `MinimumSamplingIntervals.Continuous` (`0`) for the `Value` Attribute reports **by exception** and is **not** bound by the server-wide minimum — the requested interval is returned unchanged. 3. Every other item is raised to `max(nodeMinimumSamplingInterval, MinSupportedSamplingInterval)`. Attributes other than `Value`, nodes declaring `MinimumSamplingIntervals.Indeterminate` (`-1`), and non-Variable nodes are bound only by `MinSupportedSamplingInterval`. 4. `double.MaxValue` is capped to one year (pre-existing behavior, now in one place). Event monitored items do not sample and are unaffected. **Backward compatible:** with the default of `0` the computation is identical to the previous code for every input, including `Continuous`, `Indeterminate`, positive node minimums, non-`Value` attributes and `NaN`. No existing configuration or deployment changes behavior. Node managers that implement `INodeManager` directly can call the new public helper to apply the same rule. ## Related Issues - Fixes #4191 The issue thread also asked whether `Subscription.MaxNotificationsPerPublish` is revised by the server. It is (`SubscriptionManager.CalculateMaxNotificationsPerPublish`), but it was untested and is not observable from the `CreateSubscription` response, which carries no revised field for that parameter. Four tests were added that assert it through `SubscriptionDiagnostics.MaxNotificationsPerPublish` on a privileged session, covering create and modify, the above-limit, below-limit and `0` (server default) cases. ## Tests - **`SamplingIntervalRevisionTests`** (17 unit tests, `Opc.Ua.Server.Tests`) — the full revision matrix: negative request resolving to the default interval, the `Continuous` bypass, `Indeterminate`, node minimum above *and* below the floor, a floor of `0` being a no-op, non-`Value` attributes, non-Variable and null nodes, and the `double.MaxValue` cap. - **`MinSupportedSamplingIntervalTests`** (10 integration tests, `Opc.Ua.Subscriptions.Tests`) — against a reference server configured with a 500 ms floor: the capabilities node reflects the configuration; a node declaring 100 ms is lifted to 500 ms; `Scalar_Static_Arrays2D_LocalizedText` (1000 ms) wins over the floor; `CurrentTime` (250 ms) is lifted; a `Continuous` node keeps its 10 ms; a non-`Value` attribute is floored; `ModifyMonitoredItems` behaves the same in both directions; event items are unaffected. - Restored the configuration builder and round-trip test coverage that #4194 deleted. - `TestFixture` gained a `protected virtual void ConfigureServer(ApplicationConfiguration)` hook so a derived fixture can vary the server configuration before start. Default implementation is a no-op, so no existing fixture changes behavior. ## Documentation - `docs/Subscriptions.md` — new **Sampling interval revision** section with the input table, the four-step rule (including both negative-interval fallbacks), XML and fluent configuration examples, and guidance for custom node managers. - `docs/migrate/2.0.x/configuration.md` — new section documenting that `MinMetadataSamplingInterval` was removed and `MinSupportedSamplingInterval` added. Verified against `master378`: the old setting defaulted to `1000` but was never read, and `MinSupportedSampleRate` was hardcoded to `0` with no server-wide floor. The guide therefore stresses that these are **two different settings, not a rename** — consumers should drop the old element (a default of `0` reproduces 1.5.378 exactly) and only opt into the new one deliberately, since reusing the old value would silently introduce a sampling floor. ## Validation - `dotnet build UA.slnx` — **0 warnings, 0 errors** across all TFMs. - .NET 10: `Opc.Ua.Server.Tests` 4091 passed, `Opc.Ua.Subscriptions.Tests` 619 passed (full suite including the new fixture), `Opc.Ua.InformationModel.Tests` 1063 passed, `Opc.Ua.Configuration.Tests` 221 passed, `Opc.Ua.Gds.Tests` 119 passed. - .NET Framework 4.8: new unit tests (200 incl. sampling groups), new integration tests (17) and configuration round-trip (135) all passed. - `ConsoleReferenceServer` starts successfully with the updated configuration file. ## Checklist - [x] I have signed the [CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf) and read the [CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md) doc. - [x] I have added tests that prove my fix is effective or that my feature works and increased code coverage. - [x] I have added all necessary documentation. - [x] I have verified that my changes do not introduce (new) build or analyzer warnings. - [x] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [x] I have addressed **all** PR feedback received. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 79f8090c-b2a3-409c-a5bf-56b6d2a35e70 |
||
|
|
d8aca69444 |
Remove unused MinMetadataSamplingInterval configuration option (#4194)
## Description Server-side `MonitoredItem` sampling interval revision is based on the per-node `BaseVariableState.MinimumSamplingInterval` attribute, not on any global server setting — `ServerConfiguration.MinMetadataSamplingInterval` was dead configuration that was never read by any code path. Per maintainer feedback, this PR removes it instead of wiring it into revision logic that was never meant to consume it. Separately, the concern that `Subscription.MaxNotificationsPerPublish` isn't revised by the server was verified against existing coverage: `CreateSubscriptionWithNotificationLimitsUsesEffectiveLimitAsync` / `ModifySubscriptionWithNotificationLimitsUsesEffectiveLimitAsync` in `SubscriptionTests.cs` already assert the server clamps a client-requested value down to the configured server limit (e.g. requested `25` vs. server limit `10` → revised `10`). ### Removed `MinMetadataSamplingInterval` - Dropped the `ServerConfiguration.MinMetadataSamplingInterval` property (`ApplicationConfiguration.cs`) and its XSD element - Dropped `SetMinMetadataSamplingInterval` from `IApplicationConfigurationBuilder` / `ApplicationConfigurationBuilder` - Removed the element from sample and test server configuration XML files (`Ctt.ReferenceServer.Config.xml`, `Quickstarts.ReferenceServer.Config.xml`, GDS test configs, `test-server-full.xml`) - Updated `ApplicationConfigurationBuilderTests` and `ConfigurationRoundTripTests` to drop assertions on the removed property ## Related Issues ## Checklist - [ ] I have signed the [CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf) and read the [CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md) doc. - [ ] I have added tests that prove my fix is effective or that my feature works and increased code coverage. - [ ] I have added all necessary documentation. - [ ] I have verified that my changes do not introduce (new) build or analyzer warnings. - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [ ] I have addressed **all** PR feedback received. <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #4191 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com> |
||
|
|
7cec2d614d | Move repository files to the consolidated layout |