Skip to content

background callback with MATCH is cancelled by future callbacks with different component ids #2681

@Jonas1302

Description

@Jonas1302

Describe your context

dash                        2.14.1
dash-bootstrap-components   1.5.0
dash-core-components        2.0.0
dash-extensions             1.0.3
dash-html-components        2.0.0
dash-iconify                0.1.2
dash-mantine-components     0.12.1
dash-table                  5.0.0
dash-uploader               0.7.0a1

Describe the bug
If a background callback using pattern matching with MATCH is triggered twice by two different objects (and therefore different values for MATCH), the first callback will be cancelled.

This only applies to background callbacks. "Normal" callbacks work fine.

Expected behavior
Both callbacks should finish execution and return their outputs, just like non-background callbacks. (At least if their IDs are different)

MWE
Here's a small example to reproduce the problem:

import os
import time

import diskcache
import dash
from dash import Dash, html, Output, Input, MATCH, DiskcacheManager
from dash.exceptions import PreventUpdate


def layout():
    return html.Div(
        [
            *[html.Button(f"Update {i}", id=dict(type="button", index=i)) for i in range(5)],
            *[html.P(id=dict(type="text", index=i)) for i in range(5)],
        ]
    )


@app.callback(
    Output(dict(type="text", index=MATCH), "children"),
    Input(dict(type="button", index=MATCH), "n_clicks"),
    background=True,
    prevent_initial_call=True,
)
def show_text(n_clicks: int):
    if not n_clicks:
        raise PreventUpdate

    index = dash.ctx.triggered_id["index"]
    print(f"started {index}")
    time.sleep(3)

    print(f"stopped {index}")
    return str(index)


if __name__ == "__main__":
    cache = diskcache.Cache(os.path.join(os.getcwd(), ".cache"))
    app = Dash(background_callback_manager=DiskcacheManager(cache))
    app.layout = layout()
    app.run(host="0.0.0.0", port=8010)

If you click on multiple buttons (within 3 seconds after the last click), the previous execution of the callback will be canceled and only the id of the last button will be shown.

Sample output:

started 0
started 1
started 2
started 3
started 4
stopped 4

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3backlogbugsomething brokensev-2serious problem

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions