Skip to content

ui-template: replayed messages cannot be distinguished from live messages #2192

Description

@henrikekblad

Current Behavior

When a Dashboard ui-template is loaded/reloaded, Dashboard replays the last message stored for that widget. The replayed message appears identical to the original live message: it retains the original _msgid, payload and custom properties, with no metadata indicating that it came from the Dashboard datastore.

This is problematic when the authoritative state is maintained elsewhere in Node-RED. In my case, a mailbox widget receives count: 1 when mail arrives, while the authoritative current state is stored in global context. After the mailbox is cleared, the authoritative state can be count: 0, but a newly loaded Dashboard can initially replay an older count: 1 message.

I use ui-event / $pageview to fetch the authoritative state and send it to the ui-template:

ui-event ($pageview)
    -> Fetch global state
    -> ui-template

This means two independent messages can reach the widget during page loading: the Dashboard datastore replay and the current state fetched in response to $pageview.

I have observed the replay followed by the current state, but I cannot find a documented ordering guarantee. If the two mechanisms race, simply overwriting the replay with the $pageview result is not sufficient.

The workaround I now use is to ignore all messages until an explicit fetchGlobal message initializes the widget, and to attach a state timestamp/version to backend state so that an older replay arriving after initialization can also be rejected. This works, but requires application-specific synchronization logic solely because the template cannot identify a replayed message.

Checking _msgid alone does not help: normal live Node-RED messages also have _msgid, and the replay preserves the original value.

Exported ui-template nodes also contain properties such as:

"storeOutMessages": true,
"passthru": false,
"resendOnRefresh": true

resendOnRefresh sounds related, but I cannot find a corresponding option in the current ui-template editor, and it is unclear whether this property is currently implemented/used by Dashboard 2.

Expected Behavior

For applications where Node-RED owns the authoritative state, there should ideally be a supported way to distinguish datastore replay from a new live message.

For example, replayed messages could carry metadata such as:

msg._dashboard = {
    replay: true
};

Alternatively, it would be useful to allow datastore replay to be disabled per widget, e.g. an option such as Restore last message on client load.

A documented guarantee that datastore replay always occurs before $pageview would also make explicit synchronization possible, although marking replayed messages or allowing replay to be disabled would seem more robust.

Steps To Reproduce

  1. Create a ui-template that displays a value from msg.payload.count.
  2. Send a live message to it, for example:
msg.payload = {
    count: 1,
    date: Date.now()
};
msg.mailSource = "increase";
return msg;
  1. Observe the message in the template. For example:
{
    payload: {
        count: 1,
        date: 1786717839872
    },
    topic: "",
    _msgid: "4c284f268c64dd58",
    mailSource: "increase"
}
  1. Maintain/change the authoritative application state separately (for example, set the corresponding global state to count: 0).
  2. Add a ui-event $pageview path that reads that authoritative state and sends count: 0 to the ui-template when the page loads.
  3. Reload/open the Dashboard page.
  4. Observe that the ui-template also receives the previously stored count: 1 message. From inside the template it is indistinguishable from the original live message; the original _msgid and custom properties are preserved.

Environment

  • Dashboard version: 1.30.2
  • Node-RED version: 5
  • Node.js version:
  • npm version:
  • Platform/OS:
  • Browser:

Have you provided an initial effort estimate for this issue?

I am not a FlowFuse team member

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageNeeds looking at to decide what to do

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions