Skip to content

perf: cut redundant Sparkplug payload work and bound topology-driven renders - #162

Open
bluegrassiot wants to merge 4 commits into
release/v1.0.10from
perf/sparkplug-throughput
Open

bluegrassiot wants to merge 4 commits into
release/v1.0.10from
perf/sparkplug-throughput

Conversation

@bluegrassiot

Copy link
Copy Markdown
Owner

Closes #161

What this does

Two lanes, both aimed at the same failure: throughput collapsing once Sparkplug traffic gets dense.

Core lane: receive, alias and metric work

  • Alias enrichment reused the envelope the payload pipeline had already decoded instead of reparsing the protobuf payload and reading the application message payload segment a second time for every message.
  • The payload pipeline now reads the raw payload only on failure paths, so a successful decode skips a second payload read.
  • Metric merges index the existing snapshots instead of rescanning the list for every incoming metric, keeping previous order and last-write-wins on duplicate names.

UI lane: bounded render coalescing

  • A burst of topology events drove one render per event on the nodes view and the page shell. Both now signal through a RefreshCoalescer that holds a single pending slot, so renders are bounded by the coalesce window rather than by event rate.
  • The broker thread only requests a refresh. The dispatcher reconciles the active tab or the nodes selection state and renders in the dispatched refresh, so the state a render reads is not mutated from the broker thread.
  • A refresh queued before disposal never runs, and a dispatch that throws releases the pending slot.

Tests

  • Core: alias resolution from the decoded envelope, plugin decoder fallback when TypedPayload is null or a foreign type, and in-place metric merge ordering including concurrent node data.
  • UI: coalescer unit tests plus page and view tests for burst coalescing, a blocked dispatcher backlog, dispatch failure and disposal.

Validation

  • dotnet build MqttProbe.NoMaui.slnf: 0 warnings, 0 errors.
  • Core tests: 1905 passed, 1 skipped (pre-existing platform skip).
  • UI tests: 1801 passed, 1 skipped (pre-existing platform skip).
  • Integration tests: 61 passed.
  • Coverage: 89.4% line coverage, against the 80% gate.
  • Full format check, comment check, blocking-awaits check, file-length check and the security scan all pass, along with the 361 gate-script tests (2 skipped).

Benchmark

Matched isolated runs on the Release Web host against a local Mosquitto with a headless 50 ms emulator and the same workload (about 528 to 2030 broker-delivered msg/s). At a cap of 50000 the whole-app process CPU was 72 to 82% lower, with no limiter drops. Node and detail interactions still succeed and both views are visible at the end of the run. A complete metrics scrape dropped from 5.7 to 11.4 seconds down to about 1.2 seconds. That is the whole scrape operation, not just a render.

Process CPU as a percentage of one core, by view and load:

Load Nodes view Browser view
1000 83.6 to 20.8 110.5 to 19.8
2000 132.8 to 36.5 168.7 to 34.0

Limits of this evidence

  • Higher capacity and continuous detail-value liveness are not established.
  • At a cap of 1000, throughput was unchanged and drops still appeared near the limit.
  • The original freeze was not fully reproduced, and no maximum capacity claim is made here.

Notes

  • Base branch is release/v1.0.10, not main.

Alias enrichment reparsed the Sparkplug protobuf payload and read the application message payload segment a second time for every message. The chart and store handlers now resolve aliases from the envelope the payload pipeline already decoded, and the payload pipeline itself reads the raw payload only on failure paths.

Metric merges now index the existing snapshots instead of rescanning the list for each incoming metric, keeping previous order and last-write-wins on duplicate names.

Adds regression tests for decoded-payload alias resolution, the plugin decoder fallback when TypedPayload is null or foreign, and in-place metric merge ordering under concurrency.
A burst of Sparkplug topology events drove one render per event on the nodes view and the page shell. Both now signal through a coalescer that holds a single pending slot: the broker thread only requests, the dispatcher reconciles the active tab or selection state and renders at most once per window, and a refresh still queued when the component is disposed never runs.

Adds render coalescing tests for the page and the nodes view plus direct coalescer tests, covering burst coalescing, a blocked dispatcher backlog, dispatch failure, and disposal.
@greptile-apps

greptile-apps Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; both previous findings are addressed and no new actionable issues were found.

Summary

The PR reduces repeated Sparkplug payload reads and parsing, indexes metric merges, and coalesces topology-driven renders.

  • Sparkplug alias lookup reuses the payload already decoded for each message.
  • Node and device metric updates keep their order without rescanning for every value.
  • Topology bursts refresh the page and nodes view through one pending slot.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Topology event] --> B[Request refresh]
    B --> C{Pending slot free?}
    C -->|No| D[Use existing pending refresh]
    C -->|Yes| E[Wait for coalesce window]
    E --> F[Dispatch with generation]
    F -->|Callback starts| G[Release slot and refresh latest state]
    F -->|Task fails or is canceled| H[Release slot only if generation still matches]
Loading

Reviews (2) · Last reviewed commit: "fix(ui): recover coalesced refreshes aft..." · Reviewed by Greptile

Comment thread src/MqttProbe.UI/Utilities/RefreshCoalescer.cs Outdated
Comment thread tests/MqttProbe.Core.Tests/Services/Sparkplug/SparkplugAliasResolverTests.cs Outdated
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.

1 participant