Callback is fired excessively in multi-app setup - once for each input instead of once for each callback

The struggle continues. I have two apps. One lightweight, the other more complex - but nothing too dramatic. When using multi-app support, this second one never loads, and crashes Chrome (“Aw, snap”). Run in isolation on its own, it’s fine. Unfortunately, we can only have one uwsgi instance running our dash apps, so we need to use multi-app support.

I’ve tried running it locally with Dash’s flask as well as with 5 Uwsgi processes - same issue. 10+ calls to dash-update-component per second before it keels over. Sometimes the layout loads, but rarely.

127.0.0.1 - - [14/Sep/2017 22:46:34] "POST /graph/line/_dash-update-component HTTP/1.1" 200

dash-core-components (0.12.5)
dash-html-components (0.7.0)
dash-renderer (0.9.0)

runner.py

import dash_core_components as dcc
import dash_html_components as html

from app import app
server=app.server
# Import the apps from the apps folder based on filename
from apps import line,beehives


app.layout = html.Div([
    dcc.Location(id='url', refresh=False),
    html.Div(id='page-content')
])

@app.callback(Output('page-content', 'children'),
              [Input('url', 'pathname')])
def display_page(pathname):
    if pathname == '/graph/line/line_graph':
         return line.layout
    elif pathname == '/graph/line/beehives':
    	return beehives.layout
    else:
        return '404'

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

Some of the callbacks in beehives are big - 20 inputs, all fed to one function. A lot of mysql involved, but very clean sql and no db issues. Can share the other files privately if anyone’s willing to help - I’m flat-out of ideas. The app, though complex, shouldn’t fall over like this in such a simple multi-app setup which makes me think there’s an underlying bug. Chrome’s Inspector isn’t much help unfortunately

Thanks

Will

Maybe you should try your luck with another approach to implement multi-app, check here:

Thanks, but same issues - dash dependencies load fine on one app, but loop constantly on the other. I guess I’m going to have to rethink the other app from scratch

I think that what’s happening is that when this app “appears”, each of the inputs fires the callback separately instead of all at once. So if 10 inputs are displayed for a single output, then 10 callbacks are fired instead of just once. Here’s where this is happening in the code: dash-renderer/src/actions/index.js at fe08c62c02cb6ea996c9423ebe8c76e147d39fa9 · plotly/dash-renderer · GitHub

Here’s a simple example to demonstrate this:

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

import datetime
import json
import time


app = dash.Dash()
app.config['suppress_callback_exceptions'] = True

app.layout = html.Div([
    dcc.RadioItems(
        id='toggle-content',
        options=[{'label': i, 'value': i} for i in ['Hide', 'Show']],
        value='Hide'
    ),
    html.Div(id='container')
])


@app.callback(
    Output('container', 'children'),
    [Input('toggle-content', 'value')])
def display_output(value):
    if value == 'Hide':
        return ''
    return html.Div([
        html.Div([
            dcc.Input(value='Input {}'.format(i), id='input-{}'.format(i))
            for i in range(10)
        ]),
        html.Div(id='dynamic-output')
    ])


@app.callback(
    Output('dynamic-output', 'children'),
    [Input('input-{}'.format(i), 'value') for i in range(10)])
def dynamic_output(*args):
    print(args)
    time.sleep(2)
    return [html.Strong(datetime.datetime.now()), json.dumps(args, indent=2)]


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

One way to fix this immediately would be to display all of your content upfront and just hide and show the content based off of the pathname. That is:

app.layout = html.Div([
    dcc.Location(id='ur', refresh=False),
   html.Div(id='line', children=line.layout, style={'display': 'none'),
   html.Div(id='beehives', children=beehives.layout, style={'display': 'none'})
])

@app.callback(Output('line', 'style'), [Input('url', 'pathname')])
def display_line(pathname):
    return {'display': 'block'} if pathname == '/graph/line/line_graph' else {'display': 'none'}

@app.callback(Output('beehives', 'style'), [Input('url', 'pathname')])
def display_line(pathname):
    return {'display': 'block'} if pathname == '/graph/line/beehives' else {'display': 'none'}

This will cause all of the callbacks (on all page) to fire on page load rather than just the callbacks associated with the page that is visible.

In the meantime, I’ll work on fixing this behaviour. Perhaps today, if not then early next week.

I’m working on a fix in https://github.com/plotly/dash-renderer/pull/21

Never mind, this will have the same problem. Dash is currently callbacks once per input rather than once per output. I’ll fix this as well in Dash Tabs component by chriddyp · Pull Request #74 · plotly/dash-core-components · GitHub

Thanks Chris. This makes a lot of sense and explains other similar issues I’ve had with so many inputs. I’ll look closer at the code and your gif shortly but thanks for looking at a fix. Drink tea not coffee!

1 Like

@will - I’ve just published a fix at 0.10.0-rc1 - could you give it a try? More details in the PR: https://github.com/plotly/dash-renderer/pull/21

1 Like

Hey,
I am facing the same issue and this post says it was fixed in 2017 but still I am seeing same happening to my app.
I used to handle the main page with URLs and pathname and on each pathname it is looping and sometimes produce this error as well
“An object was provided as children instead of a component, string, or number (or list of those). Check the children property that looks something like:”
Can you please guide me its kind of urgent

Thanks in advance!!!