Skip to content

[Server] Implement event-driven RolePermissionsValidation caching in MonitoredNode2 - #3583

Merged
marcschier merged 6 commits into
masterfrom
copilot/implement-event-driven-role-permissions-validation
Feb 28, 2026
Merged

marcschier merged 6 commits into
masterfrom
copilot/implement-event-driven-role-permissions-validation

Conversation

Copilot AI commented Feb 24, 2026 •

Copy link
Copy Markdown
Contributor

Proposed changes

ValidateRolePermissions was called for every monitored item on every node value change — O(clients × writes/sec) permission lookups that include a node metadata fetch per call. Under high-load scenarios this severely limits write throughput.

NodeStateChangeMasks.RolePermissions (new flag in NodeState.cs)

Added RolePermissions = 0x20 to NodeStateChangeMasks. This flag is set (alongside NonValue) when RolePermissions or UserRolePermissions change — either via property setter or an OPC UA client write (WriteNonValueAttribute). This gives subscribers a precise signal that permissions have changed without conflating it with other non-value attribute changes.

Permission result caching in MonitoredNode2

  • m_permissionCache: ConcurrentDictionary<uint, ServiceResult> — caches the ValidateRolePermissions result per monitored item ID.
  • Value changes: reads from cache; only calls ValidateRolePermissions on a cache miss.
  • NodeStateChangeMasks.RolePermissions detected: clears the entire permission cache so all items are re-validated on the next value change.
  • User identity / session change (detected in GetOrCreateContext): removes the affected item's permission cache entry alongside the existing context cache invalidation.
  • Item removal: cleans up the permission cache entry.

Namespace-level default permissions support (IConfigurationNodeManager / ConfigurationNodeManager)

Validation also depends on DefaultRolePermissions and DefaultUserRolePermissions from NamespaceMetadataState. Changes to these namespace-level defaults now also invalidate the permission cache:

  • Added event EventHandler DefaultPermissionsChanged to IConfigurationNodeManager.
  • ConfigurationNodeManager.CreateNamespaceMetadataState() subscribes to DefaultRolePermissions.StateChanged and DefaultUserRolePermissions.StateChanged on each NamespaceMetadataState. When either fires with a Value mask, DefaultPermissionsChanged is raised.
  • MonitoredNode2 subscribes to m_server.ConfigurationNodeManager.DefaultPermissionsChanged when the first monitored item is added, clears the entire permission cache when it fires, and unsubscribes when the last item is removed.
// Before: ValidateRolePermissions called on EVERY value change
ServiceResult validationResult = NodeManager.ValidateRolePermissions(operationContext, node.NodeId, PermissionType.Read);

// After: cached; only re-evaluated when node permissions, namespace default permissions, or user identity actually change
if (!m_permissionCache.TryGetValue(monitoredItem.Id, out ServiceResult validationResult))
{
    validationResult = NodeManager.ValidateRolePermissions(operationContext, node.NodeId, PermissionType.Read);
    m_permissionCache[monitoredItem.Id] = validationResult;
}

Related Issues

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Enhancement (non-breaking change which adds functionality)
  • Test enhancement (non-breaking change to increase test coverage)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected, requires version increase of Nuget packages)
  • Documentation Update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc.
  • I have signed the CLA.
  • I ran tests locally with my changes, all passed.
  • I fixed all failing tests in the CI pipelines.
  • I fixed all introduced issues with CodeQL and LGTM.
  • I have added tests that prove my fix is effective or that my feature works and increased code coverage.
  • I have added necessary documentation (if appropriate).
  • Any dependent changes have been merged and published in downstream modules.

Further comments

The RolePermissions change mask is a strict subset of NonValue — both bits are always set together when permissions change, so existing code that checks NonValue continues to work unchanged. The caching is conservative: any ambiguous invalidation trigger (identity change, context expiry, node-level RolePermissions mask, or namespace-level DefaultPermissionsChanged event) causes a full re-validation, so correctness is maintained at the cost of at most one extra validation call per trigger event.

Original prompt

This section details on the original issue you should resolve

<issue_title>[Server] Implement Event driven RolePermissionsValidation</issue_title>
<issue_description>Currently the Server checks the RolePermissions for every MonitoredItem for each ValueChange of a Node.
This is hugely inefficient and limits write performance badly under high load scenarios.
To make this more efficient NodeState needs to report changes that are relevant for RolePermissionsValidation in an Event or callback.
MonitoredNode could subscribe and only trigger Validation if the attributes relevant for permissions validation change.

This needs to be extend also from the Session / Subscription / MI to revalidate when the UserIdentity of an MI changes.</issue_description>

<agent_instructions>Please update the Server Library and the NodeState class to make use of this new architecture proposal</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@CLAassistant

CLAassistant commented Feb 24, 2026 •

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ romanett
❌ Copilot
You have signed the CLA already but the status is still pending? Let us recheck it.

…ing in MonitoredNode2

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>
Copilot AI changed the title [WIP] Optimize RolePermissionsValidation with event-driven architecture [Server] Implement event-driven RolePermissionsValidation caching in MonitoredNode2 Feb 24, 2026
@romanett

Copy link
Copy Markdown
Contributor

@copilot RolePermissions validation is more complex than that for each NamespaceMetaDataState there can be DefaultRolePermissions and also DefaultUserRolePermissions. Those can change also

…andle namespace-level default permission changes

Co-authored-by: romanett <7413710+romanett@users.noreply.github.com>

Copilot AI commented Feb 24, 2026

Copy link
Copy Markdown
Contributor Author

@copilot RolePermissions validation is more complex than that for each NamespaceMetaDataState there can be DefaultRolePermissions and also DefaultUserRolePermissions. Those can chang...

Good point. I've extended the permission cache invalidation to also cover namespace-level defaults in commit fc6d418.

What changed:

  • Added event EventHandler DefaultPermissionsChanged to IConfigurationNodeManager
  • ConfigurationNodeManager.CreateNamespaceMetadataState() now subscribes directly to DefaultRolePermissions.StateChanged and DefaultUserRolePermissions.StateChanged on each NamespaceMetadataState. When either fires with a Value mask, it raises DefaultPermissionsChanged
  • MonitoredNode2 subscribes to m_server.ConfigurationNodeManager.DefaultPermissionsChanged when the first monitored item is added and unsubscribes when the last is removed. When it fires, the entire permission cache is cleared, forcing re-validation on the next value change

@romanett
romanett marked this pull request as ready for review February 27, 2026 05:34
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.

[Server] Implement Event driven RolePermissionsValidation opc ua server unresponsive as the number of monitored items in the server increase.

4 participants