Skip to content

Chart updates are slow due to ever increasing memory #1899

@Steve-Mcl

Description

@Steve-Mcl

Current Behavior

See thread: https://discourse.nodered.org/t/dashboard-2-line-chart-data-format/99304/7?u=steve-mcl

Image

In short, setting a chart to "replace" mode does NOT clear the data in the datastore.
This leads to slowdowns and will eventually cause a browser crash

Expected Behavior

In replace mode, the data store data should be replaced

Steps To Reproduce

Compare DB1 chart to DB2 chart using this flow - witness the slowness (and gradual slowdown) of DB2 chart

Ignoring the animated updates for a second, see how after only a handful of several operations, the chart is already quite slow. (after refresh, it is better albeit animations hide that)
Image

Demo Flow

[{"id":"582afdb70610c568","type":"function","z":"96d7c957f48c57b0","name":"Generate Sine Wave Data","func":"// const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))\nconst sampleSize = msg.payload.samples || 100\nconst sineWidth = msg.payload.sineWidth\nconst amplitude = msg.payload.amplitude\nconst round = msg.payload.round || 0\nconst batchCount = msg.payload.batchCount || 1\nconst batchDelay = msg.payload.batchDelayMs || 100 // 10hz\nconst sineData = []\n\n// sanity checks\nif (typeof batchCount !== 'number' || typeof batchDelay !== 'number' || batchDelay < 0 || batchCount < 0) {\n    node.error('Invalid input: payload.n and payload.f must be postitive integers')\n    return null\n}\n\nif (typeof sampleSize !== 'number' || sampleSize <= 0 || typeof sineWidth !== 'number' || sineWidth <= 0 || typeof amplitude !== 'number') {\n    node.error('Invalid input: sampleSize, sineWidth must be positive numbers and amplitude must be a number')\n    return null\n}\n\n// Curry the round function\n/** @type {(v: Number) => Number} */\nlet rounder\nif (isNaN(+round) || +round < 0) {\n    rounder = (v) => v // dummy, do nothing, round is not valid\n} else {\n    const _round = +round\n    rounder = (v) => roundTo(v, _round)\n}\n\n// Calc total samples and populate sineData\nconst samples = sampleSize * batchCount\nfor (let i = 0; i < samples; i++) {\n    const x = i\n    const y = amplitude * Math.sin((2 * Math.PI * i) / sineWidth)\n    // const y = amplitude * Math.sin((2 * Math.PI / sineWidth) * i)\n    sineData.push({ x,  y: rounder(y) })\n}\n\n// batch the data into the payload ready for splitting\nconst batchSendSize = samples / batchCount\nconst payload = []\nfor (let iBatch = 0; iBatch < batchCount; iBatch++) {\n    const position = iBatch * batchSendSize\n    payload.push({\n        delay: iBatch * batchDelay,\n        data: sineData.slice(position, position + batchSendSize)\n    })\n}\nmsg.payload = payload\nreturn msg\n\nfunction roundTo(value, decimals) {\n    const factor = Math.pow(10, decimals)\n    return Math.round(value * factor) / factor\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1110,"y":1060,"wires":[["742c4204b659f39f"]]},{"id":"98343a89dd813e6c","type":"link in","z":"96d7c957f48c57b0","name":"sine-generator","links":[],"x":965,"y":1060,"wires":[["582afdb70610c568"]]},{"id":"742c4204b659f39f","type":"link out","z":"96d7c957f48c57b0","name":"link out 20","mode":"return","links":[],"x":1255,"y":1060,"wires":[]},{"id":"ad729387424ad27f","type":"link call","z":"96d7c957f48c57b0","name":"","links":["98343a89dd813e6c"],"linkType":"static","timeout":"30","x":1000,"y":1000,"wires":[["3bec297359769c5d"]]},{"id":"3bec297359769c5d","type":"split","z":"96d7c957f48c57b0","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","property":"payload","x":1170,"y":1000,"wires":[["9215eb2e7e137f21"]]},{"id":"e4a61a33d5fb756a","type":"delay","z":"96d7c957f48c57b0","name":"","pauseType":"delayv","timeout":"0","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":1500,"y":1000,"wires":[["96189e3791561909","3b38e741ef7e5542"]]},{"id":"9215eb2e7e137f21","type":"change","z":"96d7c957f48c57b0","name":"","rules":[{"t":"move","p":"payload.delay","pt":"msg","to":"delay","tot":"msg"},{"t":"move","p":"payload.data","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1340,"y":1000,"wires":[["e4a61a33d5fb756a"]]},{"id":"96189e3791561909","type":"ui-chart","z":"96d7c957f48c57b0","group":"7b01f09960da3d06","name":"","label":"DB2 Chart","order":1,"chartType":"line","category":"","categoryType":"none","xAxisLabel":"","xAxisProperty":"x","xAxisPropertyType":"property","xAxisType":"linear","xAxisFormat":"","xAxisFormatType":"auto","xmin":"","xmax":"","yAxisLabel":"","yAxisProperty":"y","yAxisPropertyType":"property","ymin":"","ymax":"","bins":10,"action":"replace","stackSeries":false,"pointShape":"false","pointRadius":4,"showLegend":true,"removeOlder":1,"removeOlderUnit":"3600","removeOlderPoints":"","colors":["#0095ff","#ff0000","#ff7f0e","#2ca02c","#a347e1","#d62728","#ff9896","#9467bd","#c5b0d5"],"textColor":["#666666"],"textColorDefault":true,"gridColor":["#e5e5e5"],"gridColorDefault":true,"width":"12","height":8,"className":"","interpolation":"linear","x":1670,"y":1000,"wires":[[]]},{"id":"0bafae70779c189e","type":"ui_chart","z":"96d7c957f48c57b0","name":"","group":"c593dfc95b8d6751","order":1,"width":"34","height":6,"label":"DB1 Chart","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":1670,"y":1060,"wires":[[]]},{"id":"3b38e741ef7e5542","type":"function","z":"96d7c957f48c57b0","name":"Array to DB1 Chart Data","func":"const data = msg.payload\n\nmsg.payload = [{\n    series: ['Series 1'],\n    data: [[...data]],\n    labels: ['']\n}]\n\nreturn msg","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1450,"y":1060,"wires":[["0bafae70779c189e"]]},{"id":"6e1d03cdcff5c9c0","type":"ui-button","z":"96d7c957f48c57b0","group":"7b01f09960da3d06","name":"","label":"1 batch of 1k points","order":2,"width":"4","height":"1","emulateClick":true,"tooltip":"","color":"","bgcolor":"","className":"","icon":"","iconPosition":"left","payload":"{\"batchCount\":1,\"batchDelayMs\":100,\"samples\":1000,\"amplitude\":1250,\"sineWidth\":55,\"round\":0}","payloadType":"json","topic":"topic","topicType":"msg","buttonColor":"","textColor":"","iconColor":"","enableClick":true,"enablePointerdown":false,"pointerdownPayload":"","pointerdownPayloadType":"str","enablePointerup":false,"pointerupPayload":"","pointerupPayloadType":"str","x":690,"y":880,"wires":[["42f5938b1d896612"]]},{"id":"995a0e6010f6b451","type":"inject","z":"96d7c957f48c57b0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":495,"y":880,"wires":[["6e1d03cdcff5c9c0"]],"l":false},{"id":"4f31820d4a845985","type":"ui-button","z":"96d7c957f48c57b0","group":"7b01f09960da3d06","name":"","label":"20 off, 10hz, 4k points per batch","order":4,"width":"4","height":"1","emulateClick":true,"tooltip":"","color":"","bgcolor":"","className":"","icon":"","iconPosition":"left","payload":"{\"batchCount\":20,\"batchDelayMs\":100,\"samples\":1000,\"amplitude\":2000,\"sineWidth\":130,\"round\":0}","payloadType":"json","topic":"topic","topicType":"msg","buttonColor":"","textColor":"","iconColor":"","enableClick":true,"enablePointerdown":false,"pointerdownPayload":"","pointerdownPayloadType":"str","enablePointerup":false,"pointerupPayload":"","pointerupPayloadType":"str","x":670,"y":960,"wires":[["42f5938b1d896612"]]},{"id":"9b084a8e9fdf00fe","type":"inject","z":"96d7c957f48c57b0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":495,"y":960,"wires":[["4f31820d4a845985"]],"l":false},{"id":"b71b529e50af41ae","type":"ui_button","z":"96d7c957f48c57b0","name":"","group":"c593dfc95b8d6751","order":3,"width":"6","height":"1","passthru":true,"label":"1 batch of 10k points","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"{\"batchCount\":1,\"batchDelayMs\":100,\"samples\":10000,\"amplitude\":5000,\"sineWidth\":320,\"round\":0}","payloadType":"json","topic":"topic","topicType":"msg","x":700,"y":1040,"wires":[["42f5938b1d896612"]]},{"id":"655769c4dccfd0e3","type":"ui_button","z":"96d7c957f48c57b0","name":"","group":"c593dfc95b8d6751","order":4,"width":"6","height":"1","passthru":true,"label":"20 off, 10hz, 4k points per batch","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"{\"batchCount\":20,\"batchDelayMs\":100,\"samples\":1000,\"amplitude\":2000,\"sineWidth\":130,\"round\":0}","payloadType":"json","topic":"topic","topicType":"msg","x":670,"y":1080,"wires":[["42f5938b1d896612"]]},{"id":"a508e4dcae489871","type":"inject","z":"96d7c957f48c57b0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":495,"y":1040,"wires":[["b71b529e50af41ae"]],"l":false},{"id":"cc16eda9d6a0f026","type":"inject","z":"96d7c957f48c57b0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":495,"y":1080,"wires":[["655769c4dccfd0e3"]],"l":false},{"id":"bdcc2d46d21d7ad8","type":"ui-button","z":"96d7c957f48c57b0","group":"7b01f09960da3d06","name":"1 batch of 10k points","label":"1 batch of 10k points","order":3,"width":"4","height":"1","emulateClick":true,"tooltip":"","color":"","bgcolor":"","className":"","icon":"","iconPosition":"left","payload":"{\"batchCount\":1,\"batchDelayMs\":100,\"samples\":10000,\"amplitude\":5000,\"sineWidth\":320,\"round\":0}","payloadType":"json","topic":"topic","topicType":"msg","buttonColor":"","textColor":"","iconColor":"","enableClick":true,"enablePointerdown":false,"pointerdownPayload":"","pointerdownPayloadType":"str","enablePointerup":false,"pointerupPayload":"","pointerupPayloadType":"str","x":700,"y":920,"wires":[["42f5938b1d896612"]]},{"id":"aa6671259abe677a","type":"inject","z":"96d7c957f48c57b0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":495,"y":920,"wires":[["bdcc2d46d21d7ad8"]],"l":false},{"id":"4da90c21523ca193","type":"ui_button","z":"96d7c957f48c57b0","name":"","group":"c593dfc95b8d6751","order":2,"width":"6","height":"1","passthru":true,"label":"1 batch of 1k points","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"{\"batchCount\":1,\"batchDelayMs\":100,\"samples\":1000,\"amplitude\":1250,\"sineWidth\":55,\"round\":0}","payloadType":"json","topic":"topic","topicType":"msg","x":690,"y":1000,"wires":[["42f5938b1d896612"]]},{"id":"8c929658559a038f","type":"inject","z":"96d7c957f48c57b0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":495,"y":1000,"wires":[["4da90c21523ca193"]],"l":false},{"id":"42f5938b1d896612","type":"junction","z":"96d7c957f48c57b0","x":900,"y":1000,"wires":[["ad729387424ad27f"]]},{"id":"7b01f09960da3d06","type":"ui-group","name":"Group Name","page":"d728ce64adf3bfe4","width":"12","height":1,"order":1,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"c593dfc95b8d6751","type":"ui_group","name":"Default","tab":"b51e81d1234270b6","order":1,"disp":true,"width":"34","collapse":false,"className":""},{"id":"d728ce64adf3bfe4","type":"ui-page","name":"charts","ui":"03482f22997a66ac","path":"/charts","icon":"home","layout":"grid","theme":"33d634ca6b053e0f","breakpoints":[{"name":"Default","px":"0","cols":"3"},{"name":"Tablet","px":"576","cols":"6"},{"name":"Small Desktop","px":"768","cols":"9"},{"name":"Desktop","px":"1024","cols":"12"}],"order":5,"className":"","visible":"true","disabled":"false"},{"id":"b51e81d1234270b6","type":"ui_tab","name":"Home","icon":"dashboard","disabled":false,"hidden":false},{"id":"03482f22997a66ac","type":"ui-base","name":"My Dashboard","path":"/dashboard","appIcon":"","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"headerContent":"page","navigationStyle":"default","titleBarStyle":"default","showReconnectNotification":true,"notificationDisplayTime":1,"showDisconnectNotification":true,"allowInstall":false},{"id":"33d634ca6b053e0f","type":"ui-theme","name":"Default Theme","colors":{"surface":"#ffffff","primary":"#0094CE","bgPage":"#eeeeee","groupBg":"#ffffff","groupOutline":"#cccccc"},"sizes":{"density":"default","pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}},{"id":"ddb52a58f18f5d52","type":"global-config","env":[],"modules":{"@flowfuse/node-red-dashboard":"1.29.0","node-red-dashboard":"3.6.6"}}]

Environment

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

Have you provided an initial effort estimate for this issue?

I have provided an initial effort estimate

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds-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