✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🐇 Announcing Dash VTK for 3d simulation graphics. Check out the March webinar.

Why does Live Update over small data take such a long time loading?

Just built a fairly light-weighted live dash app. It’s working but would take more than 1s to update graphs as opposed to tens of ms when updating html texts, as shown in the above screenshot. Is this normal? What could possibly go wrong here?

app.layout = html.Div(
    [
        html.H1("Sensor Dashboard"),
        html.Div(
            [
                html.Div(id="live-update-text"),
                html.Div([dcc.Graph(id="environmental-sensors-graph-pressure")]),
                html.Div([dcc.Graph(id="environmental-sensors-graph-temperature")]),
                html.Div([dcc.Graph(id="environmental-sensors-graph-humidity")]),
                dcc.Interval(
                    id="environmental-sensors-interval",
                    interval=UPDATE_INTERVAL * 1000,
                    n_intervals=0,
                ),
                html.Div(id="intermediate-value", style={"display": "none"}),
            ]
        ),
    ]
)


def plot(data, key, x_range=LIVE_FIGURE_LENGTH_SECONDS):
    value = data[key][-x_range:]
    fig = go.Figure(data=go.Scatter(y=value, mode="lines+markers", hoverinfo="y"))
    return fig


@app.callback(
    Output("live-update-text", "children"),
    [Input("environmental-sensors-interval", "n_intervals")],
)
def update_environmental_data(n_intervals):
    p, t, h = get_environmental_data()
    msg = f"Temperature: {t}\nPressure: {p}\nHumidity: {h}\n"

    return msg


@app.callback(
    Output("intermediate-value", "children"),
    [Input("environmental-sensors-interval", "n_intervals")],
    [State("intermediate-value", "children")],
)
def update_environmental_figure(n_intervals, children):
    if children is not None:
        data = json.loads(children)
    else:
        data = defaultdict(list)

    p, t, h = get_environmental_data()
    data["pressure"].append(p)
    data["temperature"].append(t)
    data["humidity"].append(h)

    return json.dumps(data)


@app.callback(
    [
        Output("environmental-sensors-graph-pressure", "figure"),
        Output("environmental-sensors-graph-temperature", "figure"),
        Output("environmental-sensors-graph-humidity", "figure"),
    ],
    [Input("intermediate-value", "children")],
)
def update_environmental_figure(children):
    data = json.loads(children)

    fig_p = plot(data, "pressure")
    fig_t = plot(data, "temperature")
    fig_h = plot(data, "humidity")
    return fig_p, fig_t, fig_h

How large is the amount of data that you are working with?

As I read your code, you are sending the data multiple times across the wire unnecessarily. You could avoid this overhead by merging the update_environmental_figure functions into one (or by storing the data server side instead of client side, e.g. either in a file, a redis cache or a dash store component).

I don’t think this issue would be attributed to data size, since I just run a local test of sample data, which is simply a dict of three lists, and each of which contains 60 items. Even then, I got a pretty big network response lag. Here’s the screenshot and the code:

app.layout = html.Div(
    [
        html.H1("Sensor Dashboard"),
        html.Div(
            [
                html.Div([dcc.Graph(id="environmental-sensors-graph-pressure")]),
                html.Div([dcc.Graph(id="environmental-sensors-graph-temperature")]),
                html.Div([dcc.Graph(id="environmental-sensors-graph-humidity")]),
                dcc.Interval(
                    id="environmental-sensors-interval",
                    interval=UPDATE_INTERVAL * 1000,
                    n_intervals=0,
                ),
            ]
        ),
    ]
)


def plot(data, key, x_range=LIVE_FIGURE_LENGTH_SECONDS):
    value = data[key][-x_range:]
    fig = go.Figure(data=go.Scatter(y=value, mode="lines+markers", hoverinfo="y"))
    return fig


@app.callback(
    [
        Output("environmental-sensors-graph-pressure", "figure"),
        Output("environmental-sensors-graph-temperature", "figure"),
        Output("environmental-sensors-graph-humidity", "figure"),
    ],
    [Input("environmental-sensors-interval", "n_intervals")],
)
def update_environmental_figure(data):
    data = defaultdict(list)
    data["pressure"] = np.random.uniform(1000, 1030, (60,)).tolist()
    data["temperature"] = np.random.uniform(0, 100, (60,)).tolist()
    data["humidity"] = np.random.uniform(0, 100, (60,)).tolist()

    fig_p = plot(data, "pressure")
    fig_t = plot(data, "temperature")
    fig_h = plot(data, "humidity")
    return fig_p, fig_t, fig_h

Also, here’s the timing breakdown of one of those requests. It seemed a good chunk of the time was Waiting (TTFB), which would largely translate to the figure generating process on the server side, right? and btw, the server is run on localhost.