Out of order callback

Hello,
I have a Slider component with updatemode=drag, and I’m seeing the callback for the slider receive callbacks that are sometimes out of order. That is, sometimes the most recent callback on the Python side actually corresponds to a value that isn’t the most recent value on the browser side.

How do I know this? In my slider callback I update a Div with the value of the slider. Interestingly, the value in the Div is always consistent with the apparent position of the slider (which is good). But when I print the value of the slider passed into the callback, I can see that the most recent value printed is not the value in the Div in some cases – it’s rare, but it happens, and it’s a problem. The fact that the 2 values – the value that’s printed in the callback is different than the value in the Div – is hugely puzzling. It’s as if on the browser side, it knows which message it receives from the callback corresponds with the most recent slider value.

I can post an example. But I’m wondering if this is a known issue and if there is a workaround or fix.

thanks!

Could you please post a (standalone) example, as you’re suggesting :-)? Thanks!

Here’s an example app. In order to reproduce, you need to have it running on a different machine than your browser. It’s dependent on CPU usage, so the app tries to simulate high CPU usage. If you grab the slider and move it around for several seconds, the callbacks will “wind-up” and become queued such that you will continue to see the prints from the callback after you stop moving the slider. In the output, you can see that the callbacks are stepping on each other (index value isn’t consistently increasing) (see below).

import dash
import dash_core_components as dcc
import dash_html_components as html
from threading import Thread
from dash.dependencies import Input, Output

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div(children=[
    html.Div(children='Slider Test'),
    html.Div(
    	dcc.Slider(id='slider', min=1, max=100, step=1, updatemode='drag'),
    	style={'max-width': 300}),
    html.Div(id='slider-val')
])

index = 0

@app.callback(
	Output('slider-val', 'children'),
	[Input('slider', 'value')])
def func(val):
	global index
	s = str(val) + ' ' + str(index)
	print(s)
	index += 1
	return s

def run():
	print('starting')
	while(1):
		pass

if __name__ == '__main__':
    thread = Thread(target=run)
    thread.start() # create bogus CPU load
    app.run_server(port=5000, host='0.0.0.0', debug=True)

Here’s output showing the index (the 2nd value) not increasing consistently.

87 806
84 806
79 808
57 808
28 810
26 811
27 812
34 813
42 814
44 815
42 816
33 816
17 817
13 819
14 820

If I put a lock in the callback (see below), the problem goes away as expected. Perhaps the mutex code can be handled in Dash.

thanks!

lock = Lock()

@app.callback(
	Output('slider-val', 'children'),
	[Input('slider', 'value')])
def func(val):
	global lock
	global index
	with lock:
		s = str(val) + ' ' + str(index)
		print(s)
		index += 1
		return s

https://dash.plot.ly/sharing-data-between-callbacks

You’re using a global variable. Unless there’s a global lock it won’t work. You need to store that value somewhere other than local memory so multiple threads have access to it.

Maybe I misunderstand your example though - are you limiting this to one thread?

Hi Russell,
The example is single thread except for a thread to create a bogus CPU load. The fact that the callback is getting reentered before a given callback call finishes seems to me to be unwanted behavior.

Is putting mutex locks in callbacks something that’s known/needed?

thanks

Hmmm. I couldn’t reproduce your error either as posted or with several other methods.

The mutex thing is not well known but it’s also never been necessary.

Well, heck, I was hoping my isolated example would yield something on your end. You’re running the example on a separate machine than your browser, yes?

The fact that I only see this under a decent CPU load is a clue to the persnicketiness of the issue (and why the example simulates CPU load.) Some issues are difficult to isolate/uncover…

I don’t have a detailed understanding what’s happening under the hood, but there should be a straightforward explanation for the concurrency of the callbacks – multiple threads?

In flask 1.0, threaded=True by default (https://stackoverflow.com/questions/38876721/handle-flask-requests-concurrently-with-threaded-true) so that might be causing issues with your global variable.

I originally thought this might be related to how we reject requests on the front-end with the request_queue (see https://github.com/plotly/dash/issues/133#issuecomment-332678387 for the original comment spawned the request_queue and it’s subsequent PRs), but that doesn’t seem like the issue here.

Thanks – I think that explains things.

So when using global/shared object/variable in a callback you should use mutex locking.

It’s best practice to just avoid globals entirely.

I’m not going to argue with that, Russell.

Do you see Dash/Plotly as strictly a data interaction and rendering tool? Or do you see it also as a way to provide browser-based interaction with other objects – say physical, processing, devices?

If so, Dash will be interacting with shared objects. I don’t see any way around it (do you?)

It’s a good question. I build whole interactive systems in Dash based around custom analysis, simulation, etc. with user profiles. I use pickle, Redis, sqlitedict, and Apache Plasma (custom solution for large files, check out brain-plasma if you’re interested) to emulte global variables but in a threadsafe and highly available way. So I guess the answer is both. :slight_smile:

That’s interesting— thanks for sharing.

I might be bending the Dash paradigm by using it as the front end for an embedded camera/vision system. :slight_smile:

I stumbled upon this:

It’s an example of Dash being used as a front end for physical hardware — it uses a global/shared variable for communication to the device (via Modbus).

I assume that applications like these may run into problems if they don’t use locks on the callbacks.

For my own understanding, is this a reasonable assumption?

thanks!

three reasons why you should buy plotly pro: support open source, get great support, host your plots and dashboards online