Callback is called before previous instance of callback is finished

Hello!

I’m having an issue with my dash program which graphs data from a SQL database in real time. Currently, I have an interval component in my layout so that the graph updates every 1.5 seconds. It will query the MySQL database for the necessary info, and then it will update my plot. But, sometimes it the graph will completely freeze and the graph will just stay blank. At which point, the graph just stays empty.

I think what is happening is that the SQL query sometimes takes longer than 1.5 seconds. So then that specific callback will wait. At the same time, the interval will hit 1.5 seconds again and the callback will once again be called, at which point, dash seems to break. I know this is true because I put a print statement before the SQL query and after the SQL query in the callback. The first print statement will sometimes occur twice. Would you anyone have any insight on this?

Right now, I’m thinking I can increase the interval to a very large number before the query, and change it back 1.5 seconds after the query, so that callbacks will wait for the previous same callback to finish before trying again.

Here’s my code for reference. Thanks in advance!

app = dash.Dash(__name__)
app.layout = html.Div([
    dcc.Input(id='start-time', type='text', value='2018-05-07 12:00:00'),
    dcc.Input(id='end-time', type='text', value='2018-05-07 12:01:00'),
    dcc.Checklist(
        id='use-time-range',
        options=[
            {'label': 'Use Time Range (Won\'t Update)', 'value': 'use_time_range'}
        ],
        values=[],
        labelStyle={'display': 'inline-block'}
    ),
    html.Div(
        style={'font-family': 'Product Sans'},
        children=dcc.Dropdown(
            id='device-dropdown',
            options=dropdown_options,
            multi=True,
            placeholder='Select the devices you want to display',
            value=[]
        ),
    ),
    dcc.Graph(id='live-update-graph'),
    dcc.Interval(
        id='interval-component',
        interval=1.5*1000, # in milliseconds
        n_intervals=0
    ),
    html.P(id='temp')
])
app.css.append_css({
    'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'
})

@app.callback(Output('live-update-graph', 'figure'),
             [Input('device-dropdown', 'value'),
              Input('interval-component', 'n_intervals'),
              Input('use-time-range', 'values')],
             [State('live-update-graph', 'figure'),
              State('start-time', 'value'),
              State('end-time', 'value')])
def update_graph_live(devices, n, use_time_range, prev_fig, start_time_range, end_time_range):
    print "We get here twice sometimes"
    #Get network and power traffic table from MySQL DB 
    if use_time_range:
        power, net_traff = get_power_and_net_traff_in_range(devices, start_time_range, end_time_range)
    elif not use_time_range:
        num_entries = 150
        power, net_traff = get_recent_n_power_and_net_traff(devices, num_entries)

    print "sometimes doesn't get here"
    fig = create_figure(devices, power, net_traff)

    return fig

if __name__ == '__main__':
    app.run_server(debug=True)
2 Likes

You can try to use some timeout to the database query and on the timeout call raise PreventUpdate() which will cancel the callback, not touching the graph. But as that timeout could never be met that may be bad idea. Better idea will be to check in subsequent callbacks if the previous finished, probably by some database session check (to check if the previous one is still alive). Again catching some exception can be made into raise PreventUpdate().

Ahh, I see. Thanks for the input! I ended up putting a decorator that would cause the method to timeout if it took longer than a set time.

1 Like