<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Archives des performance | dash-resources.com</title>
	<atom:link href="https://dash-resources.com/tag/performance/feed/" rel="self" type="application/rss+xml" />
	<link>https://dash-resources.com/tag/performance/</link>
	<description>Learn to build interactive web applications with Python and Dash plotly</description>
	<lastBuildDate>Thu, 17 Apr 2025 11:57:30 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>

<image>
	<url>https://dash-resources.com/wp-content/uploads/2024/12/cropped-dash-logo-favicon-512-32x32.png</url>
	<title>Archives des performance | dash-resources.com</title>
	<link>https://dash-resources.com/tag/performance/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>How to use allow_duplicate (good and bad practices)</title>
		<link>https://dash-resources.com/how-to-use-allow_duplicate-good-and-bad-practices/</link>
		
		<dc:creator><![CDATA[Fran]]></dc:creator>
		<pubDate>Wed, 16 Apr 2025 16:26:07 +0000</pubDate>
				<category><![CDATA[Intermediate level]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">https://dash-resources.com/?p=852</guid>

					<description><![CDATA[<p>A few weeks ago, I was inspired to write an article about the allow_duplicate options and the possible drawbacks of using it. In this new [...]</p>
<p>L’article <a href="https://dash-resources.com/how-to-use-allow_duplicate-good-and-bad-practices/">How to use allow_duplicate (good and bad practices)</a> est apparu en premier sur <a href="https://dash-resources.com">dash-resources.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>A few weeks ago, I was inspired to write <a href="https://dash-resources.com/avoiding-desynchronized-state-in-dash-sequential-callbacks-patch-and-better-design-patterns/">an article</a> about the <code>allow_duplicate</code> options and the possible drawbacks of using it.</p>



<p>In this new article, I want to soften my original take and explain when to use <code>allow_duplicate=True</code> and when not to use it with clear examples. Of course, everything you&#8217;ll read here is based on my personal experience and might not cover all use cases! <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /> </p>


        
            
            <div class="fit_content">
                <div class="bd_toc_container" data-fixedWidth="">
                    <div class="bd_toc_wrapper" data-wrapperPadding="48px">
                        <div class="bd_toc_wrapper_item">
                            <div class="bd_toc_header active" data-headerPadding="2px">
                                <div class="bd_toc_header_title">
                                    Table of Contents                                </div>
                                <div class="bd_toc_switcher_hide_show_icon">
                                    <span class="bd_toc_arrow"></span>                                </div>
                            </div>
                            <div class="bd_toc_content list-type-disc">
                                <div class="bd_toc_content_list ">
                                    <div class='bd_toc_content_list_item'>    <ul>
      <li class="first">
        <a href="#first-what-is-allow-duplicate=true">First, what is allow_duplicate=True?</a>
      </li>
      <li>
        <a href="#pros-and-cons-of-allow-duplicate=true">Pros and cons of allow_duplicate=True</a>
        <ul class="menu_level_2">
          <li class="first">
            <a href="#harder-to-track-logic-maintainability-concerns">Harder to track logic &amp; Maintainability Concerns</a>
          </li>
          <li class="last">
            <a href="#potential-race-conditions">Potential race conditions</a>
          </li>
        </ul>
      </li>
      <li>
        <a href="#good-use-example-1-separate-concern">Good use example 1: separate concern</a>
      </li>
      <li>
        <a href="#good-use-example-2-partial-update">Good use example 2: partial update</a>
      </li>
      <li>
        <a href="#bad-use-example-1-harder-to-track">Bad use example 1: harder to track</a>
      </li>
      <li>
        <a href="#bad-example-2-race-condition">Bad example 2: race condition</a>
      </li>
      <li class="last">
        <a href="#conclusion">Conclusion</a>
      </li>
    </ul>
</div>                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="layout_toggle_button">
                        <span class="bd_toc_arrow"></span>
                    </div>
                </div>
            </div>




<p>Let&#8217;s dive in!</p>



<h2 id='first-what-is-allow-duplicate=true'  id="boomdevs_1" class="wp-block-heading" >First, what is <code>allow_duplicate=True</code>?</h2>



<p>In Dash, <strong>each Output</strong> is typically tied to exactly <strong>one</strong> callback. Attempting to attach an additional callback to the same Output will normally raise an error:</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="407" src="https://dash-resources.com/wp-content/uploads/2025/04/image-1024x407.png" alt="" class="wp-image-853" srcset="https://dash-resources.com/wp-content/uploads/2025/04/image-1024x407.png 1024w, https://dash-resources.com/wp-content/uploads/2025/04/image-300x119.png 300w, https://dash-resources.com/wp-content/uploads/2025/04/image-768x305.png 768w, https://dash-resources.com/wp-content/uploads/2025/04/image.png 1242w" sizes="(max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Illustration: every Dash developer has experienced this problem of “duplicate callback output” at least once.</figcaption></figure>



<p>However, since version 2.9 Dash provides an <strong>advanced</strong> setting—<code>allow_duplicate=True</code>—that lets you break that convention. This flag, placed in the <code>Output</code> declaration, tells Dash that you <em>intend</em> to create another callback targeting the <em>same</em> Output that was already used in a different callback.</p>



<p>Here is an example:</p>



<pre class="wp-block-code"><code lang="python" class="language-python"># First callback
@app.callback(
    Output('output', 'children', allow_duplicate=True),
    Input('input-1', 'value'),
    prevent_initial_call=True,
)
def update_output_1(value):
    # ... some processing 
    return value

# Second callback (duplicate output)
@app.callback(
    Output('output', 'children'),
    Input('input-2', 'value'),
    prevent_initial_call=True,
)
def update_output_2(value):
    # ... some other processing
    return value
</code></pre>



<p>Here, both callbacks target the same <code>Output</code> component property. Because the first callback has <code>allow_duplicate=True</code>, the second is allowed to share that same Output. We could just as easily add a third one, or even more.</p>



<p class="callout">Note that the <code>allow_duplicate=True</code> is required only for the first callback. But if you change the order callbacks are written in the code, you’ll need to update it. Setting it everywhere makes it less error-prone.</p>



<p>Using this option will also requires setting <code>prevent_initial_call=True</code> to ensures the callbacks don’t get triggered simultaneously (which is what we want to avoid, as you&#8217;ll read later).</p>



<h2 id='pros-and-cons-of-allow-duplicate=true'  id="boomdevs_2" class="wp-block-heading" >Pros and cons of <code>allow_duplicate=True</code></h2>



<p>Don’t get me wrong, <strong><code>allow_duplicate=True</code> is useful.</strong></p>



<p>Sometimes we have two distinct user interactions that need to update the same component. As the number of inputs grows, we quickly end up modifying a complex login into a &#8220;mega-callback&#8221;. It therefore become a nightmare to maintain and little changes can break many behaviors.</p>



<p>Instead, <code>allow_duplicate=True</code> enables splitting the responsibilities into smaller, clearer, more isolated callbacks. </p>



<p>But this goes against the first principle: having only one output per callback, which is generally a good design principle. Not following it has potential drawbacks and trade-offs to consider <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b07.png" alt="⬇" class="wp-smiley" style="height: 1em; max-height: 1em;" />.</p>



<h3 id='harder-to-track-logic-maintainability-concerns'  id="boomdevs_3" class="wp-block-heading" >Harder to track logic &amp; <strong>Maintainability Concerns</strong></h3>



<p>Normally in Dash, each property is updated by exactly one callback, so you can always look at that callback to know “what logic is behind this value.” Breaking that rule can lead to confusion: “Wait, which callback is controlling the output now? Or is it both?”</p>


<div class="wp-block-image">
<figure class="aligncenter size-full is-resized"><img decoding="async" width="1920" height="1116" src="https://dash-resources.com/wp-content/uploads/2025/04/image-1.png" alt="" class="wp-image-854" style="width:627px;height:auto" srcset="https://dash-resources.com/wp-content/uploads/2025/04/image-1.png 1920w, https://dash-resources.com/wp-content/uploads/2025/04/image-1-300x174.png 300w, https://dash-resources.com/wp-content/uploads/2025/04/image-1-1024x595.png 1024w, https://dash-resources.com/wp-content/uploads/2025/04/image-1-768x446.png 768w, https://dash-resources.com/wp-content/uploads/2025/04/image-1-1536x893.png 1536w" sizes="(max-width: 1920px) 100vw, 1920px" /><figcaption class="wp-element-caption">Illustration: a small callback mess.</figcaption></figure>
</div>


<p>Multiple callbacks returning to the same output can significantly complicate debugging and future maintenance. Other developers (or a future you) might have difficulty quickly discerning how that one visible component is being updated in different contexts.</p>



<h3 id='potential-race-conditions'  id="boomdevs_4" class="wp-block-heading" ><strong>Potential race conditions</strong></h3>



<p>If you have multiple callbacks writing to the same component property at roughly the same time, you may get <a href="https://en.wikipedia.org/wiki/Race_condition">race conditions</a>—where whichever finishes last overwrites the other. This can cause flickering, inconsistent states, or unexpected behavior if not carefully managed.</p>


<div class="wp-block-image">
<figure class="aligncenter size-full is-resized"><img decoding="async" width="1440" height="654" src="https://dash-resources.com/wp-content/uploads/2025/04/image-2.png" alt="" class="wp-image-855" style="width:653px;height:auto" srcset="https://dash-resources.com/wp-content/uploads/2025/04/image-2.png 1440w, https://dash-resources.com/wp-content/uploads/2025/04/image-2-300x136.png 300w, https://dash-resources.com/wp-content/uploads/2025/04/image-2-1024x465.png 1024w, https://dash-resources.com/wp-content/uploads/2025/04/image-2-768x349.png 768w" sizes="(max-width: 1440px) 100vw, 1440px" /><figcaption class="wp-element-caption">Illustration: the &#8220;race condition&#8221; means the result will depend on which callback finishes first, which can lead to unexepcted results (non-deterministic).</figcaption></figure>
</div>


<p>This is something that I saw happening and I wrote an article about it in detail here: <a href="https://dash-resources.com/avoiding-desynchronized-state-in-dash-sequential-callbacks-patch-and-better-design-patterns/">The dark side of allow_duplicates &amp; making calbacks sequential</a>.</p>



<h2 id='good-use-example-1-separate-concern'  id="boomdevs_5" class="wp-block-heading" >Good use example 1: separate concern</h2>



<p>Let’s take an example of fully separated logic.</p>



<p>The following is a true example of a login/register process that I experienced in my app:</p>



<pre class="wp-block-code"><code lang="python" class="language-python">@callback(
    Output("session_id", "data", allow_duplicate=True),
    [
        Input("login_button", "n_clicks"),
        State("login_email", "value"),
        State("login_password", "value"),
    ],
    prevent_initial_call=True,
)
def user_identification(button_n_clicks, username, password):
    """This callback handles the login of a user given a username and password """
    # ... handle login
    session_id = uuid.uuid4()
    return session_id

@callback(
    Output("session_id", "data", allow_duplicate=True),
    [
        Input("register_button", "n_clicks"),
        State("register_email", "value"),
        State("register_password", "value"),
        State("register_lastname", "value"),
        State("register_firstname", "value"),
        State("register_company", "value"),
        State("register_tel", "value"),
    ],
    prevent_initial_call=True,
)
def user_registration(button_n_clicks, email, password, lastname, firstname, company, telephone):
    """This callback handles the registration of a new user"""
    # ... handle registration
    session_id = uuid.uuid4()
    return session_id
</code></pre>



<p>Both callbacks handle multiple inputs (<em>email, password, firstname, lastname</em>, etc.) and they return a <code>session_id</code>, that is stored in a session <code>dcc.Store</code>.</p>



<p>This <code>session_id</code> will then trigger another set of callbacks that are related to the loading of an user: displaying its name, loading its preferences, etc.</p>



<p>What’s interesting about this example is that it actually benefited from using <code>allow_duplicate</code>. The two callbacks are different and process a different set of inputs, thus, it makes sense to have two separate callbacks.</p>



<p>This is what it looked like if merged into one large callback:</p>



<pre class="wp-block-code"><code lang="python" class="language-python">@callback(
    Output("session_id", "data"),
    [
        Input("login_button", "n_clicks"),
        Input("register_button", "n_clicks"),
    ],
    [
        State("login_email", "value"),
        State("login_password", "value"),
        State("register_email", "value"),
        State("register_password", "value"),
        State("register_lastname", "value"),
        State("register_firstname", "value"),
        State("register_company", "value"),
        State("register_tel", "value"),
    ],
    prevent_initial_call=True,
)
def user_identification_or_registration(login_n_clicks, register_n_clicks, username, password1, email, password2, lastname, firstname, company, telephone):
    """This callback handle the login of a user given a username and password """
    if ctx.triggered_id == "login_button":
        # ... handle login
        session_id = uuid.uuid4()
        return session_id
    elif ctx.triggered_id == "register_button":
        # ... handle registration
        session_id = uuid.uuid4()
        return session_id
</code></pre>



<p>But large callbacks also have their own drawbacks: harder to maintain, multiple inputs to handle, etc… Plus, we would be sending unnecessary information with every callback trigger—making it less efficient</p>



<p>In this case, <code>allow_duplicate</code> proves really helpful in reducing the complexity and making a more readable and maintainable code.</p>



<p class="callout">Prior the introduction of <code>allow_duplicate</code>, I actually handled this with two <a href="http://dcc.Store">dcc.Store</a> : <code>session_id_login</code> and <code>session_id_register</code>, which were both triggering a callback updating a single output: <code>session_id</code>. They were used as temporary storage —but that was less efficient and required one round trip to the server as well as one unecessary callback execution.</p>



<h2 id='good-use-example-2-partial-update'  id="boomdevs_6" class="wp-block-heading" >Good use example 2: partial update</h2>



<p>The previous example illustrated how <code>allow_duplicate</code> can help separate callbacks when logic is different. It’s more about callback designing, but there are solutions to do it without <code>allow_duplicate</code>.</p>



<p>The following example <strong>could not be possible</strong> without the help of <code>allow_duplicate</code> .</p>



<p>Imagine you have a figure. And you want to update only the title of a figure without redrawing the whole figure. The code would look like:</p>



<pre class="wp-block-code"><code lang="python" class="language-python">@callback(
    Output('graph', 'figure', allow_duplicate=True),
    Input('dropdown', 'value'),
    prevent_initial_call=True,
)
def update_graph(value):
    # Download very large data from a server
    df = pd.read_csv('https://.../data.csv')
    fig = px.line(df, x='date', y='value', color='category')
    return fig

@callback(
    Output('graph', 'figure'),
    Input('title_input', 'value'),
    prevent_initial_call=True,
)
def update_title(title):
    # Just modify the title with Patch()
    fig = Patch()
    fig['layout']['title'] = title
    return fig

</code></pre>



<p>In <code>update_graph</code>, we load a large dataset and return a figure. In the other callback <code>update_title</code>, we only update the title of the figure and return it, using <code>Patch()</code>.</p>



<p>What’s wonderful here is that the use of <code>allow_duplicate</code> and <code>Patch()</code> conjointly helped updating the figure without the need to reload the whole data and create the entire figure again.</p>



<p>It’s a more efficient solution, that would translate in a more responsive application and a better user experience.</p>



<p class="callout">Lean more about partial updates here: <a href="https://dash.plotly.com/partial-properties">https://dash.plotly.com/partial-properties</a></p>



<p>In this case, <code>allow_duplicate</code> enabled something that wasn’t possible before.</p>



<h2 id='bad-use-example-1-harder-to-track'  id="boomdevs_7" class="wp-block-heading" >Bad use example 1: harder to track</h2>



<p>The following is a fictitious bad example, and/or bad callback design, where two callbacks update the same container:</p>



<pre class="wp-block-code"><code lang="python" class="language-python">@callback(
    Output("results_container", "children", allow_duplicate=True),
    Input("search_button", "n_clicks"),
    prevent_initial_call=True,
)
def search_results(n_clicks):
    """This callback searches for results when the search button is clicked"""
    if n_clicks is None:
        raise PreventUpdate
    
    # some processing
    
    return [
        html.Div("Search results from button click"),
        html.Div([html.Span(f"Result {i}") for i in range(5)])
    ]

@callback(
    Output("results_container", "children", allow_duplicate=True),
    Input("filter_dropdown", "value"),
    prevent_initial_call=True,
)
def filter_results(filter_value):
    """This callback filters results when dropdown value changes"""
    if filter_value is None:
        raise PreventUpdate
    
    # some processing
    
    return [
        html.Div(f"Filtered results for: {filter_value}"),
        html.Div([html.Span(f"Filtered item {i}") for i in range(3)])
    ]
</code></pre>



<p>Both <code>search_results</code> and <code>filter_results</code> can be triggered . If both are triggered at the same time, we will have either one result or the other (the last callback being executed), which is misleading.</p>



<p>The issue here is that we’re using one single container, <code>result_container</code>, to display two different solution. And <code>allow_duplicate=True</code> made it possible.</p>



<p class="callout">To be clear, the above code will work. It’s just that in my opinion, this is not a good use of <code>allow_duplicates</code> for the sake of maintainability and callback logic.</p>



<p>Instead, it would have been better to just have two separate containers (e.g. a new <code>search_result_container</code> and <code>filter_result_container</code>) or to merge the logic into one callback. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<h2 id='bad-example-2-race-condition'  id="boomdevs_8" class="wp-block-heading" >Bad example 2: race condition</h2>



<p>This example will illustrate the <a href="https://en.wikipedia.org/wiki/Race_condition">race condition</a> problem. </p>



<p>Imagine you need to store the number of clicks on two buttons in a list. The first button increments the first count, and the second button updates the second count:</p>



<pre class="wp-block-code"><code lang="python" class="language-python"># Our dcc store is like this:
results_store = [0, 0]

@callback(
    Output("results_store", "data", allow_duplicate=True),
    Input("button1", "n_clicks"),
    State("results_store", "data"),
    prevent_initial_call=True,
)
def update_store_from_button1(n_clicks, current_data):
    """ Store the number of clicks on button1"""
    # Sleep randomly to simulate network latency
    time.sleep(random.uniform(0.5, 2))

		# Update button 1 count
    current_data[0] = n_clicks
    return current_data

@callback(
    Output("results_store", "data", allow_duplicate=True),
    Input("button2", "n_clicks"),
    State("results_store", "data"),
    prevent_initial_call=True,
)
def update_store_from_button2(n_clicks, current_data):
    """ Store the number of clicks on button2"""
    # Sleep randomly to simulate network latency
    time.sleep(random.uniform(0.5, 2))
    
    # Update button 2 count
    current_data[1] = n_clicks
    return current_data
</code></pre>



<p>Each callback is triggered by its button, uses the state of <code>current_data</code> and returns the updated value.</p>



<p>Let’s give an example:</p>



<ul class="wp-block-list">
<li>click on button1 → … add one to the number of clicks (current data state is <code>[0, 0]</code>) … → the callback returns <code>[1, 0]</code></li>



<li>then click on button2 → … add one to the number of clicks (current data state is <code>[1, 0]</code>) … → the callback returns <code>[1, 1]</code></li>
</ul>



<p>This is all good because we click and wait for the callback to be fully executed. Now, what if we click to both buttons at the same time ?</p>



<ul class="wp-block-list">
<li>click on button1 → … add one to the number of clicks (current data state is <code>[1, 1]</code>) … → the callback returns <code>[2, 1]</code></li>



<li>click on button2 → … add one to the number of clicks (current data state is <code>[1, 1]</code>) … → the callback returns <code>[1, 2]</code></li>
</ul>



<p>Depending on which callback finishes first, we’ll get different results: [2, 1] or [1, 2].</p>



<p>You can try a live demo below (or click <a href="https://scripts.dash-resources.com/sequential/app_btns.py/">here</a>):</p>



<iframe loading="lazy" src="https://scripts.dash-resources.com/sequential/app_btns.py/" width="100%" height="400" frameBorder="0"></iframe>



<p class="callout">In this example, I chose to simulate network latency with <code>time.sleep()</code> so that the problem is emphasized. When testing locally, you might not notice the problem—but it can become obvious once deployed to production.</p>



<p>In this case, a better solution would have been to</p>



<ul class="wp-block-list">
<li>use <code>Patch()</code> to be sure we do not rely on states</li>



<li>use different <code>dcc.Store</code> to store the two information.</li>
</ul>



<p>I hope this example helped to understand race conditions. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Again, <a href="https://dash-resources.com/avoiding-desynchronized-state-in-dash-sequential-callbacks-patch-and-better-design-patterns/">this other article</a> also illustrates this problem.</p>



<h2 id='conclusion'  id="boomdevs_9" class="wp-block-heading" >Conclusion</h2>



<p>I hope this article helped you to understand what are the good and bad practices with this powerful option, <code>allow_duplicate</code>.</p>



<p>If you’re unsure when to use it, here are a few rules I’ve come up with:</p>



<div class="wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-ad2f72ca wp-block-group-is-layout-flex">
<ul class="wp-block-list">
<li>If you have two separate callback logic that depend on which inputs are triggering the callback (i.e. with <code>ctx.triggered</code>), and have a different set of inputs, then creating two callbacks with <code>allow_duplicate</code> may be a good solution <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>



<li>If the callbacks (or more) are never supposed to be triggered at the same time, you are also safe to use <code>allow_duplicate</code> but consider using different outputs if possible <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>



<li>If you need to update partially a component (e.g. a figure) using Patch, then <code>allow_duplicate</code> is your only option. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
</ul>
</div>



<p>I you have questions or want to join the discussion, I created at topic here: <a href="https://community.plotly.com/t/the-dark-side-of-allow-duplicates-making-callbacks-sequential/91459">plotly forum</a>.</p>



<p>— Happy coding! <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p></p>
<p>L’article <a href="https://dash-resources.com/how-to-use-allow_duplicate-good-and-bad-practices/">How to use allow_duplicate (good and bad practices)</a> est apparu en premier sur <a href="https://dash-resources.com">dash-resources.com</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Dash app callback performance: a real-world debugging example</title>
		<link>https://dash-resources.com/dash-app-callback-performance-a-real-world-debugging-example/</link>
		
		<dc:creator><![CDATA[Fran]]></dc:creator>
		<pubDate>Thu, 20 Feb 2025 10:07:28 +0000</pubDate>
				<category><![CDATA[Experienced level]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[tutorial]]></category>
		<guid isPermaLink="false">https://dash-resources.com/?p=756</guid>

					<description><![CDATA[<p>While working on my app, I noticed a discrepancy in performance between the deployed version and my local environment. There was a little delay to [...]</p>
<p>L’article <a href="https://dash-resources.com/dash-app-callback-performance-a-real-world-debugging-example/">Dash app callback performance: a real-world debugging example</a> est apparu en premier sur <a href="https://dash-resources.com">dash-resources.com</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>While working on my app, I noticed a discrepancy in performance between the deployed version and my local environment. There was a little delay to display some information, that I found annoying.</p>



<p>In this blog post, I’ll walk you through how I quickly enhanced my app’s performance by analyzing Dash callback execution, which hopefully give you some ideas for your debugging too. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>


        
            
            <div class="fit_content">
                <div class="bd_toc_container" data-fixedWidth="">
                    <div class="bd_toc_wrapper" data-wrapperPadding="48px">
                        <div class="bd_toc_wrapper_item">
                            <div class="bd_toc_header active" data-headerPadding="2px">
                                <div class="bd_toc_header_title">
                                    Table of Contents                                </div>
                                <div class="bd_toc_switcher_hide_show_icon">
                                    <span class="bd_toc_arrow"></span>                                </div>
                            </div>
                            <div class="bd_toc_content list-type-disc">
                                <div class="bd_toc_content_list ">
                                    <div class='bd_toc_content_list_item'>    <ul>
      <li class="first">
        <a href="#context">Context</a>
      </li>
      <li>
        <a href="#so-how-to-debug-this">So how to debug this?</a>
      </li>
      <li>
        <a href="#resolution">Resolution</a>
      </li>
      <li>
        <a href="#result">Result</a>
      </li>
      <li class="last">
        <a href="#conclusion">Conclusion</a>
      </li>
    </ul>
</div>                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="layout_toggle_button">
                        <span class="bd_toc_arrow"></span>
                    </div>
                </div>
            </div>




<h2 id='context'  id="boomdevs_1" class="wp-block-heading" >Context</h2>



<p>My dash application is essentially a real estate map which displays information when clicking on map points.</p>



<p>The click that displayed information felt instant when running locally, but when running in production, it had about a one-second delay. I also noticed that the more elements I displayed on my map, the worse the performance became.</p>



<figure class="wp-block-video"><video height="1266" style="aspect-ratio: 1694 / 1266;" width="1694" autoplay controls loop muted src="https://dash-resources.com/wp-content/uploads/2025/02/prod-app.mp4"></video><figcaption class="wp-element-caption">Illustration: you can see a 1-2sec delay between the click and the display of the information on the sidebar.</figcaption></figure>



<p>My production app and local app differ in some ways, and I could imagine at least two reasons:</p>



<ul class="wp-block-list">
<li><strong>Maybe callbacks are slower</strong> due to design differences between production and local development. For example, the cache used is not the same: I use Flask cache disk locally, which is faster, while my app uses a Redis backend to cache results. Redis introduces additional overhead due to the external request it must handle, compared to just RAM or disk caching on the same device.</li>



<li><strong>Maybe the request itself is slower</strong> due to upload/download speed limits. When deployed, my app is on a remote web server, whereas locally it runs directly on my computer. Obviously, this introduces extra overhead. It’s much faster for my browser to communicate with a local app on the same device as with a remote app over the internet.</li>
</ul>



<h2 id='so-how-to-debug-this'  id="boomdevs_2" class="wp-block-heading" >So how to debug this?</h2>



<p>To make it short, I tried to replicate the issue locally (which is always a good way to debug things). I started by inspecting the triggered callbacks after a click on the map. You might know that all Dash callbacks translate to HTTP requests, so I used the developer tools and network tab to analyze what was happening.</p>



<p class="callout">If you don’t know that callbacks translate to HTTP request, I recommend you to download my guide “<strong>Master Dash Callbacks: Architecture &amp; Best Practices (Free PDF)</strong>” <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /> find it on the sidebar &#8212;-&gt; or below this article on mobile.</p>



<p>To simulate a “distant” request from my computer to a remote server, I set the network throttling to “Good 3G” to mimic a 3G mobile connection. <strong>This was key in debugging.</strong> Without this, everything appeared snappy in my browser.</p>



<p>I discovered that eight callbacks were triggered (directly or indirectly) after clicking on the map. Here is the debugging process in video:</p>



<figure class="wp-block-video"><video height="1266" style="aspect-ratio: 1694 / 1266;" width="1694" autoplay controls loop muted src="https://dash-resources.com/wp-content/uploads/2025/02/dev-debug.mp4"></video><figcaption class="wp-element-caption">Illustration: you can see no delay in debug mode. But when simulating network throttling, the delay became visible again.</figcaption></figure>



<p>As you can see in the video, <strong>it</strong> <strong>literally took around 14 seconds</strong> (!!) to complete the eight callbacks after clicking on the map.</p>



<p>Two requests appeared to be blocking the others, leading to huge delays: between 1 and 8 seconds for some callbacks. This is significant.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="2048" height="501" src="https://dash-resources.com/wp-content/uploads/2025/02/image-9.png" alt="Illustration of the network debugging tools and the corresponding http requests to the callbacks." class="wp-image-759" srcset="https://dash-resources.com/wp-content/uploads/2025/02/image-9.png 2048w, https://dash-resources.com/wp-content/uploads/2025/02/image-9-300x73.png 300w, https://dash-resources.com/wp-content/uploads/2025/02/image-9-1024x251.png 1024w, https://dash-resources.com/wp-content/uploads/2025/02/image-9-768x188.png 768w, https://dash-resources.com/wp-content/uploads/2025/02/image-9-1536x376.png 1536w, https://dash-resources.com/wp-content/uploads/2025/02/image-9-1320x323.png 1320w" sizes="auto, (max-width: 2048px) 100vw, 2048px" /></figure>



<p>When inspecting the first request, I quickly realized that the bottleneck was the time spent “Sending” the data: 4.05s. The “Waiting” time (when the browser waits for the server response) was only 64ms. So, the callback processing was not the problem—the data upload was.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="2048" height="501" src="https://dash-resources.com/wp-content/uploads/2025/02/image-10.png" alt="Illustration of the timings for the first request." class="wp-image-760" srcset="https://dash-resources.com/wp-content/uploads/2025/02/image-10.png 2048w, https://dash-resources.com/wp-content/uploads/2025/02/image-10-300x73.png 300w, https://dash-resources.com/wp-content/uploads/2025/02/image-10-1024x251.png 1024w, https://dash-resources.com/wp-content/uploads/2025/02/image-10-768x188.png 768w, https://dash-resources.com/wp-content/uploads/2025/02/image-10-1536x376.png 1536w, https://dash-resources.com/wp-content/uploads/2025/02/image-10-1320x323.png 1320w" sizes="auto, (max-width: 2048px) 100vw, 2048px" /></figure>



<p>When inspecting the second request, I saw that it was also blocked for 4.05s, meaning it was simply not triggered immediately.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="2048" height="557" src="https://dash-resources.com/wp-content/uploads/2025/02/image-11.png" alt="Illustration of the timings for the second request." class="wp-image-761" srcset="https://dash-resources.com/wp-content/uploads/2025/02/image-11.png 2048w, https://dash-resources.com/wp-content/uploads/2025/02/image-11-300x82.png 300w, https://dash-resources.com/wp-content/uploads/2025/02/image-11-1024x279.png 1024w, https://dash-resources.com/wp-content/uploads/2025/02/image-11-768x209.png 768w, https://dash-resources.com/wp-content/uploads/2025/02/image-11-1536x418.png 1536w, https://dash-resources.com/wp-content/uploads/2025/02/image-11-1320x359.png 1320w" sizes="auto, (max-width: 2048px) 100vw, 2048px" /></figure>



<p>The third request showed the same problem as the first one: too much time spent sending data, with only 108ms processing on the server side and no delay in receiving the result.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="2048" height="555" src="https://dash-resources.com/wp-content/uploads/2025/02/image-12.png" alt="Illustration of the timings for the third request." class="wp-image-762" srcset="https://dash-resources.com/wp-content/uploads/2025/02/image-12.png 2048w, https://dash-resources.com/wp-content/uploads/2025/02/image-12-300x81.png 300w, https://dash-resources.com/wp-content/uploads/2025/02/image-12-1024x278.png 1024w, https://dash-resources.com/wp-content/uploads/2025/02/image-12-768x208.png 768w, https://dash-resources.com/wp-content/uploads/2025/02/image-12-1536x416.png 1536w, https://dash-resources.com/wp-content/uploads/2025/02/image-12-1320x358.png 1320w" sizes="auto, (max-width: 2048px) 100vw, 2048px" /></figure>



<p>At this point, it was clear that two callbacks were problematic. To identify which callbacks these HTTP requests were triggering, I inspected the request details. The names of outputs, inputs, and states are visible as part of the information Dash sends with each callback:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="2048" height="1048" src="https://dash-resources.com/wp-content/uploads/2025/02/image-13.png" alt="Illustration of debugging the inputs/states/outputs of a callback request" class="wp-image-763" srcset="https://dash-resources.com/wp-content/uploads/2025/02/image-13.png 2048w, https://dash-resources.com/wp-content/uploads/2025/02/image-13-300x154.png 300w, https://dash-resources.com/wp-content/uploads/2025/02/image-13-1024x524.png 1024w, https://dash-resources.com/wp-content/uploads/2025/02/image-13-768x393.png 768w, https://dash-resources.com/wp-content/uploads/2025/02/image-13-1536x786.png 1536w, https://dash-resources.com/wp-content/uploads/2025/02/image-13-1320x675.png 1320w" sizes="auto, (max-width: 2048px) 100vw, 2048px" /></figure>



<p>I realized that the full map data (<code>map_figure_data</code>) was being sent. For my app, that meant all the points and their hover information (descriptions, images, etc.).</p>



<p>Even though it wasn’t a huge amount of data, it posed two problems:</p>



<ul class="wp-block-list">
<li>Upload speeds are usually slower than download speeds. For slow connections like mobile networks, this delay is unbearable.</li>



<li>The issue worsens as the number of data points on my map increases (!)</li>
</ul>



<p>The second callback had the same problem. This time, the map data was an input. The goal was to see how I could eliminate this <code>map_figure_data</code> dependency.</p>



<h2 id='resolution'  id="boomdevs_3" class="wp-block-heading" >Resolution</h2>



<p>I then looked at my two callbacks. Here’s what they originally looked like.</p>



<p><strong>callback 1 &#8211; before</strong></p>



<pre class="wp-block-code"><code lang="python" class="language-python">@callback(
    Output("map_selection_memory", "data"),
    [
        Input("map", "selectedData"),
        Input("location_data_memory", "data"),
        Input("layer_selection_memory", "data"),
    ],
    [
        State("map_curve_mapping_memory", "data"),
        State("map_selection_memory", "data"),
        State("map_figure_data", "data"),  # the problematic state
    ]
)
def store_map_selection(
    selected_data, location_data, layer, curve_mapping, selected_ids, figure_data
):
    """A callback that Stores the selected ids in memory."""
</code></pre>



<p>While reviewing the callback code, I realized that <code>figure_data</code> wasn’t even used. So, I removed it and it solved the problem for this callback… (yes, I guess that these things happens!).</p>



<p><strong>callback 1 &#8211; after (removed useless state!)</strong></p>



<pre class="wp-block-code"><code lang="python" class="language-python">@callback(
    Output("map_selection_memory", "data"),
    [
        Input("map", "selectedData"),
        Input("location_data_memory", "data"),
        Input("layer_selection_memory", "data"),
    ],
    [
        State("map_curve_mapping_memory", "data"),
        State("map_selection_memory", "data"),
        # just removed the useless state
    ]
)
def store_map_selection(
    selected_data, location_data, layer, curve_mapping, selected_ids
):
    """A callback that Stores the selected ids in memory."""
</code></pre>



<p>Now, looking at the second callback.</p>



<p><strong>callback 2 &#8211; before</strong></p>



<pre class="wp-block-code"><code lang="python" class="language-python">@callback(
    Output("cache_data_metadata_memory", "data"),
    [
        Input("map_figure_data", "data"),  # this is problematic
        Input("map_selection_memory", "data"),
        Input("data_modal_unchecked_memory", "data"),
    ],
    State("address_input_memory", "data"),
    State("layer_selection_memory", "data"),
    State("filters_selection_memory", "data"),
    prevent_initial_call=True,
)
def update_cache_data_metadata_memory(
    fig_data, selected_ids, unchecked_ids, address, layer, filters
):
    """Update metadata dict (...)"""
</code></pre>



<p>Again, <code>fig_data</code> wasn’t used. However, I still needed <code>map_figure_data</code> to trigger updates, so I changed the input property to <code>modified_timestamp</code>. This enables the callback to be triggered when <code>map_figure_data</code> changes (its timestamp is updated) while not transferring the whole data.</p>



<p><strong>callback 2 &#8211; after (changed to modified_timestamp)</strong></p>



<pre class="wp-block-code"><code lang="python" class="language-python">@callback(
    Output("cache_data_metadata_memory", "data"),
    [
        Input("map_figure_data", "modified_timestamp"),  # this is efficient
        Input("map_selection_memory", "data"),
        Input("data_modal_unchecked_memory", "data"),
    ],
    State("address_input_memory", "data"),
    State("layer_selection_memory", "data"),
    State("filters_selection_memory", "data"),
    prevent_initial_call=True,
)
def update_cache_data_metadata_memory(
    map_update_timestamp, selected_ids, unchecked_ids, address, layer, filters
):
    """Update metadata dict (...)"""
</code></pre>



<p>And that did the job perfectly!</p>



<p><strong>But what if I actually had to keep the full figure data</strong> ? I would have had two choices:</p>



<ul class="wp-block-list">
<li>Create another input, that is lighter than the figure data</li>



<li>Process the figure data in a clientside callback to avoid a roundtrip to the server.</li>
</ul>



<h2 id='result'  id="boomdevs_4" class="wp-block-heading" >Result</h2>



<p>These changes literally took 20 seconds to implement. The result was instantly better, even with the &#8220;Good 3G&#8221; mobile throttling still enabled:</p>



<figure class="wp-block-video"><video height="1266" style="aspect-ratio: 1694 / 1266;" width="1694" autoplay controls loop muted src="https://dash-resources.com/wp-content/uploads/2025/02/prod-app-fixed.mp4"></video><figcaption class="wp-element-caption">Illustration: no more delay on the debug app, even with 3G network throttling. </figcaption></figure>



<p>After making these adjustments, I ensured there were no other instances of <code>map_figure_data</code> being used as either an Input or Output.</p>



<h2 id='conclusion'  id="boomdevs_5" class="wp-block-heading" >Conclusion</h2>



<p>When debugging this issue, I thought it would be an interesting use case to share, because: </p>



<ul class="wp-block-list">
<li>Using <strong>developer tools</strong> is a powerful way to debug slow callbacks and sluggish Dash apps. Keep in mind that all inputs and states trigger data uploads, which in some cases can be extremely slow.</li>



<li>Using <strong>throttling mode</strong> allows you to simulate production conditions or at least non-optimal conditions, unlike debugging locally.</li>
</ul>



<p>I hope you found this interesting. Such performance explanations and debugging techniques are part of my <a href="https://dash-resources.com/dash-plotly-course/"><strong>Dash course</strong></a>, where you can find more <strong>detailed explanations and videos</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>If you have questions, feel free to ask them <a href="https://community.plotly.com/t/dash-app-callback-performance-a-real-world-debugging-example/90653">here</a> on Plotly&#8217;s forum.</p>



<p>Happy coding! <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>L’article <a href="https://dash-resources.com/dash-app-callback-performance-a-real-world-debugging-example/">Dash app callback performance: a real-world debugging example</a> est apparu en premier sur <a href="https://dash-resources.com">dash-resources.com</a>.</p>
]]></content:encoded>
					
		
		<enclosure url="https://dash-resources.com/wp-content/uploads/2025/02/prod-app.mp4" length="378456" type="video/mp4" />
<enclosure url="https://dash-resources.com/wp-content/uploads/2025/02/dev-debug.mp4" length="1929505" type="video/mp4" />
<enclosure url="https://dash-resources.com/wp-content/uploads/2025/02/prod-app-fixed.mp4" length="785372" type="video/mp4" />

			</item>
	</channel>
</rss>
