Hi,
I created a Dash application that plots some data from a FastAPI source.
I used EventSourceResponse
from sse_starlette
because the data might come at different intervals (not faster than 100ms).
Here is my simplified FastAPI (for testing purposes):
import asyncio
import json
import uvicorn
from sse_starlette import EventSourceResponse
from starlette.applications import Starlette
from starlette.middleware import Middleware
from starlette.middleware.cors import CORSMiddleware
middleware = Middleware(CORSMiddleware, allow_origins=["*"], allow_headers=["*"])
server = Starlette(middleware=[middleware])
async def random_data():
while True:
await asyncio.sleep(0.1)
timestamp = int(time.time()*1000)
send_msg = {
'timestamp': timestamp,
}
for j in range(8):
send_msg["var_" + str(j)] = (math.sin((timestamp + j) / 1000) + j)
yield json.dumps(send_msg)
@server.route("/random_data")
async def sse(request):
generator = random_data()
return EventSourceResponse(generator)
if __name__ == "__main__":
uvicorn.run(server, port=5000)
In my dash app, I declared an EventSource
component from the dash_extensions
module as follow:
EventSource(id="sse-1", url="http://127.0.0.1:5000/random_data")
with a client-side callback to bypass the Dash server:
update_graph = """function(msg) {
if (!msg) {
return {};
}
const data = JSON.parse(msg);
const x = [new Date(data.timestamp)];
const y = [
[data.var_0],
[data.var_1],
[data.var_2],
[data.var_3],
[data.var_4],
[data.var_5],
[data.var_6],
[data.var_7],
];
return [
{
x: Array(8).fill(x),
y: y,
},
[0, 1, 2, 3, 4, 5, 6, 7],
1000,
];
}
"""
app.clientside_callback(update_graph,
Output('stress-test-graph-1', 'extendData'),
Input("sse-1", "message"))
And my graph is declared as follow:
dcc.Graph(id='stress-test-graph-1',
figure=go.Figure(data = [go.Scattergl(
x = [], y = [], mode = 'lines'
)]*8,
layout=go.Layout(
xaxis=dict(fixedrange=True, type='date'),
margin=dict(l=20, r=20, t=20, b=20))),
)
I used Scatergl
to get better performances, but Google Chrome is using a lot of CPU (I5-8250U, 16Go RAM):
I think Dash won’t be able to measure the ressources used by my callback, as it is client-side and it shows 0:
(the figure callback is used at page load to load the data history from a Redis cache).
Right now my benchmark produces 8 series in a chart, with 60% CPU average with spikes at 100%, and I might have to provide even more charts in my app.
Is there a way to improve performances in my app?
- I thought about using Holoviews, but it is so different, I’m not sure I can use it with Dash
- I didn’t want to create an Interval fetching data from the Redis cache because I don’t want to miss any data point and I have a lot of uncertainty about my data source (it’s going to be MQTT mostly)
- I thought about sending data in batches from my API, but I need a real-time-like UX so I’ll need to do some work in Javascript anyway to render an illusion of real-time
Or am I using the wrong tool to develop my app?
Thanks in advance.