How can I force transmission of a node value on next poll?

483 views
Skip to first unread message

Andrew Thomas

unread,
Mar 21, 2017, 6:15:33 PM3/21/17
to open62541
I am creating a UA server using version 0.2.  I have a situation where I know that a particular NodeId needs to be retransmitted to every client, even though it appears not to have changed according to the UA server internals.  Knowing the NodeId, how can I tell the server internals that the value for this NodeId is dirty and needs to be retransmitted?

This situation comes about because I am using data source variable nodes (UA_Server_addDataSourceVariableNode) for all of my data items.  In my server I watch for a change in a value for a node called "Handshake".  When Handshake changes from 0 to 1, the server performs an action and then sets Handshake back to 0.

I believe the sequence of events is this:

* Handshake is 0 in both client and server
* Server knows that that the last value it sent to Client is 0
* Client successfully writes a 1 to Handshake.  It now knows that Handshake = 1
* Server receives the 1, does its action and sets Handshake to 0
* Server poll cycle occurs.  Server sees that Handshake is 0, and the last write to Client was 0, so it does nothing.
* Client goes on forever thinking that Handshake is 1 because it was never told otherwise.

After the fourth step the server needs to invalidate its knowledge of the last value sent to the client so that it will re-send a 0.  How can I do this?

Andrew Thomas

unread,
Mar 21, 2017, 8:18:49 PM3/21/17
to open62541
Thinking about this, it's too crude to invalidate the value for all clients. It would be better to invalidate the lastSampledValue for every subscription associated with the client who wrote the 1 to the server.

The server is maintaining lastSampledValue for every MonitoredItem.  This essentially means that the server is maintaining knowledge about the client's value for each NodeId.  When the client successfully writes a value to a node, the server now "knows" that this client believes the value for that node to have changed.  The server could update the lastSampledValue for every MonitoredItem for that NodeId in every subscription owned by that client to the newly written value.

In general, is it reasonable to always update lastSampledValue for every MonitoredItem for a NodeId when a client writes a value to that NodeId?  Or would it be better to invalidate the value in lastSampledValue for all subscriptions owned by that client?


Julius Pfrommer

unread,
Mar 22, 2017, 7:39:43 AM3/22/17
to open62541
Hello Andrew,

our implementation of Subscriptions and DataChangeNotifications follows the OPC UA standard.
There is no mechanism in the standard that takes into account the source of a change (who performed the write).
So you would have to implement a custom extension. You can do it with open62541, being open source and all, but that sounds like a lot of work.

You can either use a third value 2 for the acknowledgement or use timestamps.
When writing a value, you can set a sourceTimestamp value.
The MonitoredItem for DataChanges can have different triggers:

    UA_DATACHANGETRIGGER_STATUS = 0,
    UA_DATACHANGETRIGGER_STATUSVALUE = 1,
    UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP = 2

When you trigger for the status, value and timestamp, you will be notified whenever the timestamp of the value changes.
In the client, you know which timestamp you are responsible for and can filter out the notification.

I hope this helps.

Best regards,
Julius

Andrew Thomas

unread,
Mar 22, 2017, 9:32:58 AM3/22/17
to open62541
Thank you for the reply, Julius.

This problem cannot be solved in the client.  In the sequence above the client is not aware that the value has changed in the server.  The client has sent a 1 to the server, so it thinks, correctly, that the server contains a 1.  The server then modifies the value to 0, but never transmits that 0 back to the client.  The reason for this is that the server thinks, falsely, that the client contains a 0 because that is the last value the server sent to the client.  It is not taking into account that the client has since transmitted a 1.

In effect, the server is maintaining a cache of the client's value in lastSampledValue.  However, the server does not know that this cache has become dirty, which will happen every time a client writes a value to the server.  This is a problem that will affect all clients that write to items that they also monitor.  Think of this as a cache coherency problem in the server.

There needs to be a mechanism in the server that can mark lastSampledValue as invalid so the server will transmit its current value.

I think this could be added to Service_Write:

if (UA_Server_editNode succeeds)
{
    loop for all UA_Subscription in the current UA_Session
        loop for all UA_MonitoredItem in UA_Subscription
            if (monitoredItem.itemID == nodeBeingWritten)
            {
                UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
                monitoredItem->lastSampledValue = UA_BYTESTRING_NULL;
            }


That would invalidate the server's cache and cause the value to be retransmitted on the next polling cycle.

Perhaps better would be to assign monitoredItem->lastSampledValue = the value just written;

I accept that the specification says nothing on this problem.  This is an implementation choice that is resulting in inconsistent results.  Looking at the OPC Foundation's C# reference implementation I can see that they handle this problem gracefully by flagging a MonitoredItem as needing transmission independently of the current value.  If a node value changes more than once during a polling cycle then any change in value will mark the MonitoredItem as dirty and it will stay dirty until the value is retransmitted.

In the case of DataSourceVariableNodes there is no specific event to mark the value as having changed, so we need a way to manually mark the MonitoredItem as dirty.  That can either be handled during the Service_Write as above, or it can be handled with an additional API call like "UA_RetransmitMonitoredItems(UA_Service, UA_Session)".  The problem with the API option is that UA_Session is not available at any point in my code where I'm aware that the lastSampledValue cache should be invalidated.

That is why I asked for advice:
* The problem is server-side
* The problem is deep - it is a cache coherence failure
* It affects all clients that write values to the server
* It requires knowledge that is hidden from user code

Best regards,
Andrew

Andrew Thomas

unread,
Mar 22, 2017, 9:47:56 AM3/22/17
to open62541
Hi again, Julius.

As an example of why I think this problem has a larger importance, here is a way to make any server fail just with two innocent clients, even when the server does not modify the value itself:

* Image we have clients A and B, server S and item P.
* At the start, S, A and B all have the value 0 for P.  They are consistent.
* S contains lastSampledValue = 0 for A and B
* A and B have update cycles of 1000 ms
* A sends P = 1 to S
* 500 ms later, B sends P = 0 to S
* Now, S and B contain P = 0.  A contains P = 1
* The polling tick occurs.  S examines lastSampledValue for A and B and determines that no change needs to be transmitted.
* The system is stable with
        S => P = 0
        B => P = 0
        A => P = 1

The server has failed to transmit a value change to A that was strictly necessary.  The control system fails inexplicably.

Best regards,
Andrew

Julius Pfrommer

unread,
Apr 1, 2017, 3:00:21 AM4/1/17
to open62541
> The control system fails inexplicably.

To me, it fails with a quite good explanation.

I think this scenario mis-uses the DataChangeNotification mechanism.
One cannot expect any mechanism with a sampling interval to handle the case described below.

In an automation context, the OPC UA server is usually close to the physical process.
Higher-level systems, such as SCADA, MES and ERP applications typically have clients consuming the data.
So there is a "single source of truth" regarding measurements from the process.

Besides DataChangeNotifications, OPC UA defines Events. Events are discrete and go directly into the publishing queue of the subscription.
So they do not require a sampling interval. However, open62541 does not (yet) support events.

Best regards,
Julius

Andrew Thomas

unread,
Apr 1, 2017, 7:22:04 AM4/1/17
to open62541
This is not a mis-use of DataChangeNotification at all.  It is exactly what any server is intended to do.  The server acts, as you say, as the "single source of truth" for all of its clients. In this simple scenario, the server is not providing the truth to one of its clients.

This is not the responsibility of the client. Both clients have correctly registered an interest in P.  It is the server's job to keep them both up to date.  There is no way for a client even to detect that this scenario has happened.

In a dumb server, we could simply make the server always send ever value on every poll.  This would guarantee that the clients always have correct values.  This is a mechanism that guarantees correctness while using a polling interval.

The challenge, then, is to make the server efficient while also maintaining correctness.  OPC UA does this by only sending those values to a client that the server knows have changed since the last polling cycle.  That is a perfectly valid optimization if the server is correct in its decision about what has changed.

Simply checking the current value against the previously transmitted value is efficient, but it is not correct.  The above example illustrates where it fails.

I think this problem is simply fixed.  Add one more bit of information to a MonitoredItem indicating that the MonitoredItem is "dirty".  Set the dirty bit whenever there is a change to the ItemId managed by the MonitoredItem, and reset it whenever the server successfully sends a value to the client.  This could be further optimized by only setting the dirty bit when the new value for the ItemId is different from lastSampledValue.

This would mean that if A contains the value 0, and B writes 0, then A receives no update.  However, if A contains 0 and B writes a sequence of 0, 1, 0 then A will receive an update even though the update contains a 0. 

It would change the example above to look like this:


* At the start, S, A and B all have the value 0 for P.  They are consistent.
* S contains lastSampledValue = 0 for A and B
* A and B have update cycles of 1000 ms
* A sends P = 1 to S
    * S sets the dirty flag for P for both A and B

* 500 ms later, B sends P = 0 to S
    * S does not set the dirty flag for A and B, since both lastSampledValues are already 0
    * Both A and B still have their dirty flags set from A's previous write of 1

* Now, S and B contain P = 0.  A contains P = 1
* The polling tick occurs.  S examines the dirty flag for A and B.
    * S sends P = 0 to both A and B
    * S resets the dirty flag for P for both A and B

* The system is stable and correct with

        S => P = 0
        B => P = 0
        A => P = 0

This results in an unnecessary "change" being transmitted to B, since there is no reliable way for S to track whether A or B really needs to be updated.  So there is an efficiency cost to maintaining correctness, but it is a small price to pay to avoid having the control system fail.

This would also offer a solution for the original problem that I faced.  If the server library exposed an API that allowed user code to manually set the dirty bit for all (or selected) MonitoredItems for a given ItemId, then even a complex server could maintain correctness.

The OPC Foundation's C# implementation solves this problem by maintaining a "dirty list".  Any time an ItemId value changes, all MonitoredItems for that ItemId are added to a list of items (on their respective Subscriptions) to be transmitted on the next polling cycle.  That acts the same as a dirty bit, but it is memory intensive.  Considering that open62541 can target memory-constrained devices, I think a dirty bit is more desirable.

Best regards,
Andrew

Julius Pfrommer

unread,
Apr 1, 2017, 11:00:37 AM4/1/17
to open62541
Hello there,

first of all, there are two different concepts at play here:
  • MonitoredItems, which generate notifications
  • Subscriptions, which store incoming notifications (possibly from multiple MonitoredItems) in a queue until sending them out in a single batch

The messages going out to the client are decoupled from the rate at which notifications are created.


I checked the OPC UA spec on this. The full spec can be downloaded on the homepage of the OPC Foundation.

Accounts can be registered with just a valid email address. Actually, there is quite a bit of flexibility in the allowed behaviour.


The following are relevant excerpts from part 4, §5.12.1.2 Sampling Interval.

In general, sections §5.12.1 MonitoredItem Model and §5.13.1 Subscription Model should provide all the answers.


The assigned sampling interval defines a “best effort” cyclic rate that the Server uses to sample
the item from its source. “Best effort” in this context means that the Server does its best to sample
at this rate. Sampling at rates faster than this rate is acceptable, but not necessary to meet the
needs of the Client. How the Server deals with the sampling rate and how often it actually polls its
data source internally is a Server implementation detail. However, the time between values
returned to the Client shall be greater or equal to the sampling interval.


The fastest supported sampling interval may be equal to 0, which
indicates that the data item is exception-based rather than being sampled at some period.


Note that, in many cases, the OPC UA Server provides access to a decoupled system and
therefore has no knowledge of the data update logic. In this case, even though the OPC UA
Server samples at the negotiated rate, the data might be updated by the underlying system at a
much slower rate.


Currently, the MonitoredItem can only test for value changes by invoking the read service and comparing the value with the last result.

We could add a hook or "exception mechanism" that updates the MonitoredItems at every write, as described above.

But we currently don't have the exception mechanism in the code and always use the sampling interval.

  • "Write exceptions" are not a general solution, since many embedded systems simply map GPIO pins for physical sensor readings to a memory address. So we need to manually perform a read anyway.
  • What happens when we read two different values within the interval (a change from 0 to 1 and then again a change from 1 to 0)? As described above, we cannot send both values as this would violate the requirement that samples are not taken at a faster interval than negotiated.
  • We could add "write exceptions" quite easily. But we have no performance bottleneck that requires them. I can subscribe to several 10.000 values in parallel without a significant uptick in CPU consumption.

What you describe sounds like a perfect use case for Events, as opposed to DataChangeNotifications.

Events are discrete. So once they are thrown, they end up in the publication queue and no sampling is required.


Best regards,
Julius

Andrew Thomas

unread,
Apr 1, 2017, 12:46:26 PM4/1/17
to open62541
Hello Julius,


Currently, the MonitoredItem can only test for value changes by invoking the read service and comparing the value with the last result.

That is precisely the problem.  The MonitoredItem is not sufficiently sophisticated to deal with the simple case of two writers.  It fails to update one of them.  This is an implementation detail of the server, resulting in a failure to maintain consistency between the server and the client.  I have outlined one possible solution.  I'm sure there are others.

For the sake of simplicity, ignore my earlier proposal of watching writes from a subscription.  That just complicates the conversation.

I don't think Events help here. In order for that to be useful, the client would need to know all of the following:
* the server is unable to maintain consistency through subscriptions
* the system contains interactions that would result in inconsistency, based on the server's internal implementation
* the server provides a mechanism through Events to mitigate this inconsistency.
The client would then need a way to associate Events to subscribed items in order to rectify inconsistency problems at the client side.

This is essentially throwing the correction of a server flaw back at the client, and would require all clients to maintain special code specifically to deal with open62541 servers.  I don't see that as practical.

Given that other servers solve this problem, why not also solve it in open62541?

Best regards,
Andrew

Julius Pfrommer

unread,
Apr 3, 2017, 3:31:01 AM4/3/17
to open62541
Hello Andrew,

the problem is quite well known in the literature. Albeit in slightly different contexts, such as memory management.
https://en.wikipedia.org/wiki/ABA_problem

To cite again from the spec:

However, the time between values returned to the Client shall be greater or equal to the sampling interval.

I fear that no compliant OPC UA server can support this use case via DataChangeNotifications. When the data updates are faster than the sampling interval, some data (history) must get lost.

If data updates are not continuous (with "infinite" updates per secod), the complete history could theoretically be transmitted.
But if some updates are faster than the sampling rate, notifications need to be produced that do not rely on a sampling rate.
Events would be the perfect solution here. Just create an event whenever the value is written into. We already provide the hooks for the callback.

In the language of distributed systems theory, the sever sending out data updates in discrete event notifications would provide a linearization point.
https://en.wikipedia.org/wiki/Linearizability

If you have an idea with regards to implementation or on how to phrase a clear message on the consistency guarantees of the technology, that would be very welcome.

Best regards,
Julius

Julius Pfrommer

unread,
Apr 3, 2017, 3:36:55 AM4/3/17
to open62541
Just some last thoughts.


The MonitoredItem is not sufficiently sophisticated to deal with the simple case of two writers.

Did you mean DataChange-MonitoredItems or MonitoredItems in general?

There are two kinds of MonitoredItems. DataChanges (supported in open62541) and Events (not yet supported).
Subscriptions are the "transport" mechanism that carries notifications from (possibly several) DataChanges and Events.

Best regards,
Julius

Andrew Thomas

unread,
Apr 3, 2017, 8:25:52 AM4/3/17
to open62541
Hi Julius,

I meant DataChange-MonitoredItems.

Best regards,
Andrew

Andrew Thomas

unread,
Apr 3, 2017, 9:12:49 AM4/3/17
to open62541
Hi Julius,

Actually, this is not the ABA problem.  In the ABA problem, the issue is that one of the participants cannot be made aware of an intermediate value that existed and then was subsequently overwritten within a single polling cycle.  The issue that I'm highlighting here is that a participant is not aware of a final value 

In the ABA problem one of the clients is not aware of the full history of the data value, but all clients end in a state where they agree on the current value of the data. This problem is more serious - it stabilizes in a state where the clients do not agree on the current value.  You can look at this problem as closer to an eventual consistency problem: https://en.wikipedia.org/wiki/Eventual_consistency

In the traffic light example on the ABA Wikipedia page, it is as if the first driver sees a red light forever when all of the other drivers see green.

I agree that it is neither necessary nor desirable to maintain history to solve this problem.  We certainly don't want an embedded server to be performing unbounded buffering. But, the clients need a rock-solid guarantee that eventually the server will send the most current value, thereby ensuring that the system is consistent in its "final" state.  The current implementation does not provide this guarantee.

The UA specification (Part 4 - 5.12.1.5) states that MonitoredItems will maintain a queue of values to be transmitted, and new values will replace old values in the queue if the queue overflows.  This specified algorithm completely solves this problem, even if the queue depth is 1.  That is why this issue does not occur in the OPC Foundation's sample servers.  The algorithm that I have suggested above does not require a queue - a dirty bit in the MonitoredItem acts like a queue of length 1.  Once the MonitoredItem is on the queue, it remains on the queue until it is transmitted, even if its value returns to the original value before it is actually transmitted.  Relying on lastSampledValue alone acts like a queue of length 1 where the queue can be cleared before it is transmitted.

It is clear that you don't believe that this is a legitimate server responsibility.  As far as I can tell it is not detectable or solvable in the client (think about third-party clients who have no knowledge of the server's internal implementation).  In my estimation it is sufficiently important that it should never be installed in a production system.  But don't take my opinion alone.  Please show this conversation to somebody whose knowledge you trust.  I would be very interested to hear other perspectives on it.

Best regards,
Andrew

Julius Pfrommer

unread,
Apr 3, 2017, 11:02:06 AM4/3/17
to open62541
Hey Andrew,

aah, alright. From the first post in this thread I assumed this was all about ABA.
I fully agree that eventual consistency needs to be achieved. It was good of you to drill down into the issue.


The UA specification (Part 4 - 5.12.1.5) states that MonitoredItems will maintain a queue of values to be transmitted, and new values will replace old values in the queue if the queue overflows.  This specified algorithm completely solves this problem, even if the queue depth is 1.

Yes. That is the way our implementation works as well. The protocol even lets you choose whether to overwrite the oldest or the newest value when the queue overflows.
And the client needs to acknowledge every receipt. Until then, the client can ask for a retransmission of a missed notification to get the full history.
But that is the queue of generated notifications, not "dirty-bits" that tell us to generate a notification.


In the sequence above the client is not aware that the value has changed in the server.  The client has sent a 1 to the server, so it thinks, correctly, that the server contains a 1.

Right. The DataChange-MonitoredItem only compares the current sample with the previous notification and not with "what they client may think it is".
Sampling strictly adheres to the sampling interval.
We could add special handling that checks whether a client is monitoring the value it just wrote into. I will discuss this possibility with the other developers.
And we have to see whether this breaks tests with the official conformance testing tools. They have quite substantial tests on DataChange-MonitoredItems (which we all pass).

Your use case may still covered by the current implementation. Just set the DataChangeTrigger of the MonitoredItem to Status+Value+Timestamp.
Then a notification is generated whenever the (source) timestamp has changed. In your example, the client would then get a notification with the latest value even when the value has not changed between samples due to ABA.

I hope this resolves the issue. Otherwise, just keep the posts coming. :-)

Best regards,
Julius

Andrew Thomas

unread,
Apr 3, 2017, 1:02:17 PM4/3/17
to open62541
Hi Julius,


We could add special handling that checks whether a client is monitoring the value it just wrote into. I will discuss this possibility with the other developers.

If this check modified lastSampledValue then it would solve the specific example above.  It might make the code a little bit fragile, since it would now be overloading the meaning of lastSampledValue.  A dirty bit performs a slightly different function by explicitly marking the MonitoredItem as needing to be transmitted, regardless of lastSampledValue. User code in the server could then schedule the MonitoredItem for transmission based on its own needs.

The original problem - value changes that originate inside the server - would not be handled just by monitoring writes from clients.  In this case there would still be a need for a public API call in the open62541 library that allows user code in the server to set the dirty bit (or invalidate lastSampledValue) on all MonitoredItems for a given ItemId, on all Subscriptions for all clients.

These two things together - invalidating or updating lastSampledValue/dirty-bit on client writes, plus a public API for server code - would allow a server to make a guarantee of eventual consistency.


Your use case may still covered by the current implementation. Just set the DataChangeTrigger of the MonitoredItem to Status+Value+Timestamp.

That is true, but only for clients that know that the problem exists.  All clients in the system would have to do this, and triggering on Timestamp is extremely inefficient.  I'm not concerned about my immediate application, but rather looking forward to using open62541 for long-lived projects, so having a robust server is a definite win.

Thanks for being patient and for looking into this.  Would you like me to add an issue to the GitHub project?

Best regards,
Andrew
Reply all
Reply to author
Forward
0 new messages