Skip to content

[BUG] Long Callbacks: Raising Error Causes Infinite Loop (Including PreventUpdate) #1821

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Robbie-Palmer opened this issue Oct 28, 2021 · 3 comments · Fixed by #2039
Closed

Comments

@Robbie-Palmer
Copy link

Environment

python               3.9.7
dash                 2.0.0

Describe the bug

If an error is raised within a long callback, an infinite loop of re-raising this error is triggered, as a new process tries again and again
This is triggered by the _long_callback_interval_1.n_intervals property

Expected behavior

The error would be handled like with normal callbacks, and the long callback wouldn't be called by new processes

Minimum Reproducible Example

import time

import dash
import diskcache
from dash import html
from dash.dependencies import Input, Output
from dash.long_callback import DiskcacheLongCallbackManager

cache = diskcache.Cache("./cache")
long_callback_manager = DiskcacheLongCallbackManager(cache)

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

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


@app.long_callback(
    output=Output("paragraph_id", "children"),
    inputs=Input("button_id", "n_clicks"),
    running=[
        (Output("button_id", "disabled"), True, False),
    ],
)
def callback(n_clicks):
    if n_clicks == 2:
        raise dash.exceptions.PreventUpdate(dash.callback_context.triggered[0]['prop_id'])
    time.sleep(2.0)
    return [f"Clicked {n_clicks} times"]


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

In the above example, can safely click the button once, then on the second click raising PreventUpdate causes the infinite loop
With logged messages of:

Process Process-3:
Traceback (most recent call last):
  File "./python3.9/site-packages/multiprocess/process.py", line 315, in _bootstrap
    self.run()
  File "./python3.9/site-packages/multiprocess/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "./python3.9/site-packages/dash/long_callback/managers/diskcache_manager.py", line 143, in job_fn
    user_callback_output = fn(*maybe_progress, *user_callback_args)
  File "./reprex_long_callback_infinite_loop_on_error.py", line 31, in callback
    raise dash.exceptions.PreventUpdate(dash.callback_context.triggered[0]['prop_id'])
dash.exceptions.PreventUpdate: button_id.n_clicks
Process Process-4:
Traceback (most recent call last):
  File "./python3.9/site-packages/multiprocess/process.py", line 315, in _bootstrap
    self.run()
  File "./python3.9/site-packages/multiprocess/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "./python3.9/site-packages/dash/long_callback/managers/diskcache_manager.py", line 143, in job_fn
    user_callback_output = fn(*maybe_progress, *user_callback_args)
  File "./reprex_long_callback_infinite_loop_on_error.py", line 31, in callback
    raise dash.exceptions.PreventUpdate(dash.callback_context.triggered[0]['prop_id'])
dash.exceptions.PreventUpdate: _long_callback_interval_1.n_intervals
Process Process-5:
Traceback (most recent call last):
  File "./python3.9/site-packages/multiprocess/process.py", line 315, in _bootstrap
    self.run()
  File "./python3.9/site-packages/multiprocess/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "./python3.9/site-packages/dash/long_callback/managers/diskcache_manager.py", line 143, in job_fn
    user_callback_output = fn(*maybe_progress, *user_callback_args)
  File "./reprex_long_callback_infinite_loop_on_error.py", line 31, in callback
    raise dash.exceptions.PreventUpdate(dash.callback_context.triggered[0]['prop_id'])
dash.exceptions.PreventUpdate: _long_callback_interval_1.n_intervals
Process Process-6:
Traceback (most recent call last):
  File "./python3.9/site-packages/multiprocess/process.py", line 315, in _bootstrap
    self.run()
  File "./python3.9/site-packages/multiprocess/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "./python3.9/site-packages/dash/long_callback/managers/diskcache_manager.py", line 143, in job_fn
    user_callback_output = fn(*maybe_progress, *user_callback_args)
  File "./reprex_long_callback_infinite_loop_on_error.py", line 31, in callback
    raise dash.exceptions.PreventUpdate(dash.callback_context.triggered[0]['prop_id'])
dash.exceptions.PreventUpdate: _long_callback_interval_1.n_intervals
Process Process-7:
...
@Robbie-Palmer Robbie-Palmer changed the title Long Callbacks: Raising Error Causes Infinite Loop (Including PreventUpdate) [BUG] Long Callbacks: Raising Error Causes Infinite Loop (Including PreventUpdate) Oct 28, 2021
@SteveYgq
Copy link

SteveYgq commented Nov 3, 2021

我遇见了同样的问题,无法正常使用,请求解决方案,我使用案例文档案例也出现了同样的问题

代码如下:
import time
import dash
from dash import html
from dash.long_callback import CeleryLongCallbackManager
from dash.dependencies import Input, Output
from celery import Celery

celery_app = Celery(
name, broker="redis://localhost:6379/0", backend="redis://localhost:6379/1"
)
long_callback_manager = CeleryLongCallbackManager(celery_app)

app = dash.Dash(name, long_callback_manager=long_callback_manager)

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

@app.long_callback(
output=Output("paragraph_id", "children"),
inputs=Input("button_id", "n_clicks"),
running=[
(Output("button_id", "disabled"), True, False),
(Output("cancel_button_id", "disabled"), False, True),
],
cancel=[Input("cancel_button_id", "n_clicks")],
)
def callback(n_clicks):
time.sleep(2.0)
return [f"Clicked {n_clicks} times"]

if name == "main":
app.run_server(debug=True)

效果是这样的
1
2

@isCopyman
Copy link

I have a similar problem, I click the button which will fire a long_callback, when the long_callback is finished, it will be fired again. A infinite loop

@kiwifoxtrot
Copy link

I have a similar problem, I click the button which will fire a long_callback, when the long_callback is finished, it will be fired again. A infinite loop

If the callback takes longer than 1000ms to download to the browser, it will continuously loop. In the dash.py file, there is a hardcoded setting for interval_time. Try setting it to 10000ms instead, that fixed my issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants