I am attempting to use dash to interact with a set of data. There are 50 or so different items in this set of data.
I have build an all-in-one component to display each item of data in a dash datatable, sitting inside a dbc ‘card’. This AIO component is working really well - we can feed a list of names of SQL tables into the app and it will render 50 different pieces of data with minimal code.
The issue starts when the data changes. Something in the app triggers picking up a different version of each piece of data (same structure, just a different version, e.g. some exchange rates from last year vs this year).
The app has a callback that changes a dcc.Store within all of the AIO components. The AIO component has a callback using MATCH that then gets the new data. Having this callback at the level of the AIO component is really useful, as this same callback also allows the user to click a button and add a row to the data.
The querying of the new data from SQL is quick based on my profiling, maybe a couple of seconds across all 50 items. However, dash is taking ages to render all of the data, maybe 40 to 60 seconds. I think this is because each callback is its own interaction between client and server, so there is a lot of time spent going between the two, even though the calculation on the server is very quick.
Question 1 is this - if one action will trigger many callbacks, does dash trigger one interaction between server and client for each callback, or does dash work out all the information it will need back from the server in all callbacks that will be triggered and make a single request, this only going across the network once?
Assuming the first option is correct, it seems to be inefficient to have many callbacks triggered by one action as opposed to consolidating those callbacks into one. If that is the case, an option to optimise my app would be to make a single callback outside of the AIO component which updates the data in all AIO components. Question 2 - any other ideas for optimisation here?
The final issue (Question 3) is that each AIO component has its own ‘add row’ button for the datatable. If I move the ‘update data’ callback to the parent app, am I no longer able to have the ‘add row’ functionality within my AIO component due to mutliple callbacks hitting the same output?
In summary:
Is each callback a new server <-> client interaction, or is there one server <-> client interaction for each action?
Any suggestions for optimising my code with callbacks inside the many AIO components
Can I have both the parent callback talking to my AIO component data whilst retaining the AIO component’s ‘add row’ button?
If you are running in debug mode, you can expand the “debug options” (blue button in the bottom right side) and select “callback” to see the Callback Graph. Each rounded green rectangle is a callback and if you click it, you can see some info like data transfer and average execution time. This is very useful for situations as you are, where you want to optimize your callback graph.
It is a bit hard to understand exactly what your code does without an example. Am I correct reading that you have one dcc.Store component inside each AIO component, that they update all in a single callback, which then triggers an “internal” callback to update the card (based on the dcc.Store data)? If that is the case, then I think the “internal” callback is triggered 50 times and that’s where the bottleneck is.
I haven’t used a lot of the AIO pattern, so take my suggestions with a grain of salt. Hopefully someone with more experience can give you a better answer to the questions.
That said, I imagine it is possible to have clientside callbacks on AIO components. If so, then my suggestion would be to make the dcc.Store → dbc.Card on the client side. The reason is because the data is already there, so you avoid sending it back and forth to the server. There are two caveats though:
clientside callbacks are not async, so they block the browser window. For quick updates it is fine, but 50 components might be a lot.
If your “add row” button triggers a callback to insert data in the DB, then you have to make it server side (they wouldn’t be the same callback I guess…).
Thanks so much for your response. Just to clarify what is going on and add a couple of further questions:
Each AIO has its own Store
There is a callback in the main app that updates the store in every instance of the AIO
Then, the AIO does a call to an underlying SQL database to query the relevant data based on that store
For this reason, the callback inside the AIO can’t be clientside
The functionality of adding a row is only on the client side. (There is then functionality to update the data in the database with an explicit button click, but that isn’t the bottleneck in my app right now, so I didn’t mention it.)
I could make this addrow functionality clientside only. Will I have issues of two different callbacks updating the same thing? (i.e. addrow updates the data in the datatable, as does the dcc.Store → DataTable
Regarding the question on your last point, it will be a problem. However, you can have a single callback to handle both situations by using the callback context. Here is the documentation about it, in case you are not familiar with it:
Then, the AIO does a call to an underlying SQL database to query the relevant data based on that store
Are these the callbacks updating the dcc.Store? If yes, then you are safe to use clientside callbacks on the one updating dcc.Store → DataTable.
The issue with using callback context is that the suggested approach to resolving the performance issues is raising the update data callback to the parent component (and using AIO’s ‘ALL’ keyword), whilst the addrow callback needs to exist at the level of each AIO (using the AIO’s ‘MATCH’ keyword