Enhance publish request count management to allow limiting the publish requests to a max count (#2681)

* Enhance publish request count management

- Added `MaxPublishRequestCount` property to `ISession` and `Session` in `Opc.Ua.Client`, allowing control over the maximum number of publish requests per session, with validation against predefined constants `kDefaultPublishRequestCount` and `kMaxPublishRequestCountMax` (set to `ushort.MaxValue`).
- Introduced `kMaxPublishRequestCountMax` constant in `Session` class for the maximum publish request count limit.
- Modified `Session` constructor to initialize `m_maxPublishRequestCount` with `kMaxPublishRequestCountMax`.
- Updated `StartPublishing` and `QueueBeginPublish` methods in `Session` to use `GetDesiredPublishRequestCount` for dynamic adjustment of publish requests, considering the session's current state and the new min/max limits.
- Added `GetDesiredPublishRequestCount` method in `Session` to calculate the optimal number of active publish requests, ensuring adherence to the newly established limits.
- Made minor adjustments and refactoring in `Session` for clarity, consistency, and to support the new publish request count management logic.

* Fix build

* Lower logging level for debug level message

* PR feedback
This commit is contained in:
Marc Schier
2024-07-31 20:18:55 +03:00
committed by GitHub
parent d967bd5277
commit 784dde674b
3 changed files with 79 additions and 16 deletions
@@ -277,6 +277,11 @@ namespace Opc.Ua.Client
/// </summary>
int MinPublishRequestCount { get; set; }
/// <summary>
/// Gets and sets the maximum number of publish requests to be used in the session.
/// </summary>
int MaxPublishRequestCount { get; set; }
/// <summary>
/// Stores the operation limits of a OPC UA Server.
/// </summary>
+67 -16
View File
@@ -55,6 +55,7 @@ namespace Opc.Ua.Client
{
private const int kReconnectTimeout = 15000;
private const int kMinPublishRequestCountMax = 100;
private const int kMaxPublishRequestCountMax = ushort.MaxValue;
private const int kDefaultPublishRequestCount = 1;
private const int kKeepAliveGuardBand = 1000;
private const int kPublishRequestSequenceNumberOutOfOrderThreshold = 10;
@@ -286,6 +287,7 @@ namespace Opc.Ua.Client
m_keepAliveInterval = 5000;
m_tooManyPublishRequests = 0;
m_minPublishRequestCount = kDefaultPublishRequestCount;
m_maxPublishRequestCount = kMaxPublishRequestCountMax;
m_sessionName = "";
m_deleteSubscriptionsOnClose = true;
m_transferSubscriptionsOnReconnect = false;
@@ -876,6 +878,29 @@ namespace Opc.Ua.Client
}
}
}
/// <summary>
/// Gets and sets the maximum number of publish requests to be used in the session.
/// </summary>
public int MaxPublishRequestCount
{
get => Math.Max(m_minPublishRequestCount, m_maxPublishRequestCount);
set
{
lock (SyncRoot)
{
if (value >= kDefaultPublishRequestCount && value <= kMaxPublishRequestCountMax)
{
m_maxPublishRequestCount = value;
}
else
{
throw new ArgumentOutOfRangeException(nameof(MaxPublishRequestCount),
$"Maximum publish request count must be between {kDefaultPublishRequestCount} and {kMaxPublishRequestCountMax}.");
}
}
}
}
#endregion
#region Public Static Methods
@@ -4954,12 +4979,7 @@ namespace Opc.Ua.Client
/// </summary>
public void StartPublishing(int timeout, bool fullQueue)
{
int publishCount = GetMinPublishRequestCount(true);
if (m_tooManyPublishRequests > 0 && publishCount > m_tooManyPublishRequests)
{
publishCount = Math.Max(m_tooManyPublishRequests, m_minPublishRequestCount);
}
int publishCount = GetDesiredPublishRequestCount(true);
// refill pipeline. Send at least one publish request if subscriptions are active.
if (publishCount > 0 && BeginPublish(timeout) != null)
@@ -5249,7 +5269,8 @@ namespace Opc.Ua.Client
private void QueueBeginPublish()
{
int requestCount = GoodPublishRequestCount;
int minPublishRequestCount = GetMinPublishRequestCount(false);
int minPublishRequestCount = GetDesiredPublishRequestCount(false);
if (requestCount < minPublishRequestCount)
{
@@ -5257,7 +5278,7 @@ namespace Opc.Ua.Client
}
else
{
Utils.LogInfo("PUBLISH - Did not send another publish request. GoodPublishRequestCount={0}, MinPublishRequestCount={1}", requestCount, minPublishRequestCount);
Utils.LogDebug("PUBLISH - Did not send another publish request. GoodPublishRequestCount={0}, MinPublishRequestCount={1}", requestCount, minPublishRequestCount);
}
}
@@ -5544,8 +5565,8 @@ namespace Opc.Ua.Client
/// <summary>
/// Find and return matching application description
/// </summary>
/// <param name="endpointDescriptions">The descriptions to search through</param>
/// <param name="match">The description to match</param>
/// <param name="endpointDescriptions">The descriptions to search through</param>
/// <param name="match">The description to match</param>
/// <param name="matchPort">Match criteria includes port</param>
/// <returns>Matching description or null if no description is matching</returns>
private EndpointDescription FindMatchingDescription(EndpointDescriptionCollection endpointDescriptions,
@@ -5877,7 +5898,7 @@ namespace Opc.Ua.Client
}
}
// Check for outdated sequence numbers. May have been not acked due to a network glitch.
// Check for outdated sequence numbers. May have been not acked due to a network glitch.
if (latestSequenceNumberToSend != 0 && availableSequenceNumbers?.Count > 0)
{
foreach (var sequenceNumber in availableSequenceNumbers)
@@ -6183,12 +6204,14 @@ namespace Opc.Ua.Client
}
/// <summary>
/// Returns the minimum number of active publish request that should be used.
/// Returns the desired number of active publish request that should be used.
/// </summary>
/// <remarks>
/// Returns 0 if there are no subscriptions.
/// </remarks>
private int GetMinPublishRequestCount(bool createdOnly)
/// <param name="createdOnly">False if call when re-queuing.</param>
/// <returns>The number of desired publish requests for the session.</returns>
protected virtual int GetDesiredPublishRequestCount(bool createdOnly)
{
lock (SyncRoot)
{
@@ -6197,6 +6220,8 @@ namespace Opc.Ua.Client
return 0;
}
int publishCount;
if (createdOnly)
{
int count = 0;
@@ -6212,11 +6237,36 @@ namespace Opc.Ua.Client
{
return 0;
}
return Math.Max(count, m_minPublishRequestCount);
publishCount = count;
}
else
{
publishCount = m_subscriptions.Count;
}
return Math.Max(m_subscriptions.Count, m_minPublishRequestCount);
//
// If a dynamic limit was set because of badTooManyPublishRequest error.
// limit the number of publish requests to this value.
//
if (m_tooManyPublishRequests > 0 && publishCount > m_tooManyPublishRequests)
{
publishCount = m_tooManyPublishRequests;
}
//
// Limit resulting to a number between min and max request count.
// If max is below min, we honor the min publish request count.
// See return from MinPublishRequestCount property which the max of both.
//
if (publishCount > m_maxPublishRequestCount)
{
publishCount = m_maxPublishRequestCount;
}
if (publishCount < m_minPublishRequestCount)
{
publishCount = m_minPublishRequestCount;
}
return publishCount;
}
}
@@ -6388,6 +6438,7 @@ namespace Opc.Ua.Client
private bool m_reconnecting;
private SemaphoreSlim m_reconnectLock;
private int m_minPublishRequestCount;
private int m_maxPublishRequestCount;
private LinkedList<AsyncRequestState> m_outstandingRequests;
private readonly EndpointDescriptionCollection m_discoveryServerEndpoints;
private readonly StringCollection m_discoveryProfileUris;
@@ -230,6 +230,13 @@ namespace Opc.Ua.Client
set => m_session.MinPublishRequestCount = value;
}
/// <inheritdoc/>
public int MaxPublishRequestCount
{
get => m_session.MaxPublishRequestCount;
set => m_session.MaxPublishRequestCount = value;
}
/// <inheritdoc/>
public OperationLimits OperationLimits => m_session.OperationLimits;