plotly / dash

Data Apps & Dashboards for Python. No JavaScript Required.
https://plotly.com/dash
MIT License
21.46k stars 2.07k forks source link

Long callback function executed without being triggered in Dash Pages with Background callback #2369

Open RunQi-Han opened 1 year ago

RunQi-Han commented 1 year ago

Background callback works fine with Dash Pages. However, if we use a dcc.Store() (defined in the app.py) as an Output in the background callback, a long callback function is executed upon page load without being triggered.

[2022-12-21 18:54:55,564: INFO/MainProcess] Task long_callback_145c7444e8da7496daf8e16142713cc0c5b46902[224f59f9-aabe-4e60-942b-16f23fad09ee] received
[2022-12-21 18:54:57,582: INFO/ForkPoolWorker-1] Task long_callback_145c7444e8da7496daf8e16142713cc0c5b46902[224f59f9-aabe-4e60-942b-16f23fad09ee] succeeded in 2.0161583395674825s: None

I have attached a sample app to reproduce this issue. app.py

from dash import Dash, html, dcc, DiskcacheManager, CeleryManager, Input, Output, html
import dash
from celery import Celery
import os

if 'REDIS_URL' in os.environ:
    # Use Redis & Celery if REDIS_URL set as an env variable
    from celery import Celery
    celery_app = Celery(__name__, broker=os.environ['REDIS_URL'], backend=os.environ['REDIS_URL'])
    background_callback_manager = CeleryManager(celery_app)
    print('using Redis caching')
else:
    # Diskcache for non-production apps when developing locally
    import diskcache
    cache = diskcache.Cache("./cache")
    background_callback_manager = DiskcacheManager(cache)

app = Dash(__name__, use_pages=True,background_callback_manager=background_callback_manager)

app.layout = html.Div([
    html.H1('Multi-page app with Dash Pages'),
    html.Div(
        [
            html.Div(
                dcc.Link(
                    f"{page['name']} - {page['path']}", href=page["relative_path"]
                )
            )
            for page in dash.page_registry.values()
        ]
    ),
    dcc.Store(id='main-store'),
    dash.page_container
])

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

pages/background_callback.py

import time
import dash
import pandas as pd
import json
import plotly.express as px
from dash import Output, Input, State, html, dcc, callback

dash.register_page(__name__,path='/')

layout = html.Div(
    [
        html.Div([html.P(id="paragraph_id", children=["Button not clicked"])]),
        html.Button(id="button_id", children="Run Job!"),
    ]
)

@callback(
    Output("paragraph_id", "children"),
    Output('main-store','data'),
    Input("button_id", "n_clicks"),
    background=True,
    prevent_initial_call=True
)
def update_clicks(n_clicks):
    time.sleep(2.0)
    return [f"Clicked {n_clicks} times"], [f"Clicked {n_clicks} times"]

DOKKU_SCALE

web=1
queue=1

requirements.txt

dash==2.7.0
gunicorn==20.0.4
pandas==1.1.4
diskcache
celery==5.2.7
redis==4.3.4

Procfile

web: gunicorn app:server --workers 4
queue: celery -A app:celery_app worker --loglevel=INFO --concurrency=2
RunQi-Han commented 1 year ago

it might be related to https://github.com/plotly/dash/issues/1985