← Articles
OPC UA/7 min read/ views

How OPC UA Republish Recovers Dropped Notifications Before They Become Trend Gaps

Sequence numbers and the Republish service turn a dropped notification into zero data loss — but only if the client watches them and the queue is sized.

OPC UASCADAHMINetworkingTroubleshooting

A lost publish is not always a lost value

An OPC UA subscription does not simply push data changes over TCP and hope they arrive. Every NotificationMessage carries a sequence number, and the server keeps recently sent messages in a retransmission queue. If a client notices a gap in the sequence numbers, it can ask the server to send the missing message again with the Republish service.

This matters because a dropped or reordered PublishResponse does not have to mean lost data. On a clean network it rarely triggers. On a marginal link, a busy server, or a client that briefly stops calling Publish, it is the difference between a trend with a real hole in it and a trend that quietly recovered.

The trap is that many clients log a reconnect or a "subscription recovered" line and never tell you whether any notifications were actually lost. If you do not check the sequence numbers and the Republish counters, you are trusting that the recovery was clean when it may not have been.

Where sequence numbers come from

Each subscription numbers its NotificationMessages sequentially, starting at 1 and incrementing by one for every message that carries notifications. This numbering, and the Republish service that leans on it, are defined in OPC UA Part 4 (Services, IEC 62541-4) — it is not vendor-specific magic, so a compliant server on the other end will support it. Keepalive messages do not consume a sequence number, which is a common source of confusion when reading a trace.

The client acknowledges received messages in the next Publish request. Once acknowledged, the server is free to drop that message from its retransmission queue. Until acknowledged, the message stays available for Republish, bounded by the queue size.

So there are three moving parts:

  • The next sequence number the server will send.
  • The retransmission queue, holding sent-but-unacknowledged messages.
  • The client acknowledgements, riding along on each Publish request.

A gap is when the client receives sequence number N and then N+2 without ever seeing N+1.

Detecting a gap

The client is responsible for noticing the gap. The server will not warn you. Practical detection:

  • Track the last received sequence number per subscription.
  • On each NotificationMessage, compare against last+1.
  • If the new number is higher than expected, one or more messages are missing in between.
  • If the new number is lower or equal, you are seeing a duplicate or a reordered message; do not treat it as a gap.

Reordering does happen, especially across a gateway or a NAT boundary that can retransmit. Build the check so a late-arriving N after N+1 is handled as a duplicate, not counted as two separate faults.

Calling Republish

When the client detects that N+1 is missing, it calls Republish with the subscription id and the missing sequence number. Possible outcomes:

  • Success — the server still had the message in its retransmission queue and returns it. The client processes those notifications as if they had arrived normally, in order.
  • BadMessageNotAvailable — the message has already been dropped, usually because the queue was too small or too much time passed. This notification is genuinely lost.
  • BadSubscriptionIdInvalid — the subscription no longer exists; you are past recovery and into re-creating the subscription.

Republish handles one sequence number per call. To recover several missing messages, call it repeatedly, in order, until you reach a number the server can no longer supply or you have caught up. Stop on the first BadMessageNotAvailable — earlier messages will not still be there if a later one is already gone.

Why gaps actually happen

The mechanism is simple; the causes are the usual field problems.

  • The client stopped calling Publish. OPC UA is pull-based for notifications: the server can only send when it holds a Publish request from the client. If the client's Publish pipeline stalls (GC pause, blocked thread, slow disk write in the historian path), the server queues notifications and the client falls behind.
  • Publish queue overflow on the server. The server holds only so many pending Publish requests and so many queued messages. If the client under-supplies Publish requests, the server eventually sets the moreNotifications flag or drops to keepalive behavior, and sequence continuity can break.
  • Transport loss or reset. A dropped secure channel, a reconnect, or a middlebox that resets idle connections can lose in-flight PublishResponses.
  • Retransmission queue too small. Even when Republish is called correctly, a queue that only holds a couple of messages cannot help if the client was behind by more.

Client-side settings that decide recovery

You do not tune Republish directly, but several related settings decide whether it can succeed.

  • Number of outstanding Publish requests. The client should keep several Publish requests in flight, not one. A common guideline is at least as many as the number of subscriptions plus a margin, so the server always has a request available to answer. One outstanding request is the classic cause of self-inflicted gaps.
  • Publishing interval vs. client processing time. If notifications arrive faster than the client can process and acknowledge, the backlog grows. Match the interval to what the client can realistically drain.
  • Retransmission queue size (server side where configurable). Large enough to cover the worst realistic reconnect window at the current publishing rate.
  • Lifetime count and keepalive count. If the subscription expires because the client went quiet too long, you lose the whole subscription, not just a message, and Republish cannot help.

Commissioning and troubleshooting checklist

  • Client tracks and logs the last sequence number per subscription, and logs a gap explicitly, not just "recovered."
  • On a detected gap, the client calls Republish for each missing sequence number in order, and stops on BadMessageNotAvailable.
  • A genuinely lost notification (BadMessageNotAvailable) is surfaced as bad-quality or a trend gap, not silently skipped.
  • Reordered/duplicate messages are handled as duplicates, not double-counted as faults.
  • The client keeps multiple Publish requests outstanding; confirm this in a packet capture, not just in config.
  • Publishing interval is achievable given client processing and acknowledgement time under load.
  • Retransmission queue is sized for the worst reconnect window at the current rate.
  • Keepalive and lifetime counts are set so a brief client pause does not expire the subscription.
  • Republish success and failure counts are visible in diagnostics or logs, so a link that quietly relies on Republish is noticed before it starts failing.

Prove the recovery path before you trust it

Left unmonitored, a subscription can drop notifications for weeks while every recovery log still reads clean — the whole failure mode is silent by design. So don't take Republish on faith.

Force a gap. In a lab or a maintenance window, pause the client's Publish loop for a few seconds while a value is changing, or firewall off the return traffic long enough to overrun the in-flight PublishResponses, then look at the trend. If it recovers with no hole, your sequence tracking and Republish path are doing their job. If there's a gap, either the retransmission queue was too small or the client never actually called Republish — and you learned it on a Tuesday afternoon instead of during an audit six months later.