[Solved] Plot seems to cache and doesn't grab up to date data on refresh (or first navigation)

Hello All,

I have some odd behavior from one of my Dash apps. After my app has been running for some time (~2 days), it won’t get the most up to date data from my database until after the refresh period (which in my case is long [5 mins]). If I restart the app, it begins to work correctly again (this amounts to killing the running pod). I was wondering if anyone has experienced this before?

It only happens with my app here: https://github.com/barrymoo/crc-status-dash-donut (series of pie charts), but not https://github.com/barrymoo/crc-status-dash (a line-points plot). Running at https://crc-status-donut.k8s.barry-moore-ii.com and https://crc-status.k8s.barry-moore-ii.com, respectively. Any thoughts are appreciated.

Thanks,

Barry

1 Like

I believe that the dcc.Interval component only fires an update after the interval period has passed, not at the beginning. So, if you want an update to appear at the start, you probably have to make your app.layout a function and return an updated value on page load.

Please read the entire post. If I restart the pod, it works as expected. Additionally, if you look at the code I am doing this.

You suggested this to me on another post: [Solved] Using a long interval, render once before interval starts. Which is working great most of the time.

Yes, I understand. That’s because the queries are run when the app starts, not when someone visits the page. https://github.com/barrymoo/crc-status-dash-donut/blob/master/app.py#L143 in your code creates the layout at app start not on page visit, hence “caching” the results. So, you need to make this a function so that the function is called on page visit to recompute the queries. See the “Updates on page load” section here: Live Updates | Dash for Python Documentation | Plotly

By this I mean:

app.layout = generate_layout

not

app.layout = generate_layout()

Essentially, the layout function can’t accept parameters?

The point is that app.layout is pointing to the function itself, not to what the function returns. Additionally, yes, if app.layout is assigned a function then it can’t take any parameters.
That shouldn’t be an issue because your parameters (labels) are already defined above. You could just rewrite it like:

def serve_layout():
    return generate_layout(labels)

app.layout = serve_layout
1 Like

I probably had a naked generate_layout and then decided to abstract over the labels. This feels like a “gotcha”.

Yeah, it seems to trip some people up. Do you have any suggestions?

What if you just forced app.layout to be a function? Anyone with app.layout = function(args) would just need to change to app.layout = lambda: function(args).

Something like:

class App:
    def _get_layout(self):
        return self.layout;
    def _set_layout(self, value):
        if not callable(value):
            raise TypeError("app.layout must be a function")
        self.__layout = value
    layout = property(_get_layout, _set_layout)

app = App()
app.layout = lambda x: x # OK
app.layout = 1 # Not OK

I thought about that, however I like the simplicity, especially when getting started with dash, of seeing

app.layout = html.Div([
   'Hello World'
])

rather than

def serve_layout():
   return html.Div([
       'Hello World'
   ])

app.layout = serve_layout

There are also advantages of having a pre-evaluated layout. Namely, that you don’t have to evaluate it on every page load, if for some reason it’s computationally expensive. Although layout generation probably won’t be usually. A more compelling reason I think is that it means you catch bugs in your layout generation on app load rather than when a user visits the site, which makes for an easier debugging experience.

I think the appropriate solution is improved documentation of dynamic layouts. Perhaps this wants to be moved, or just added to the main part of the guide. Arguably this isn’t advanced usage, and including it as another option for building a layout will make it more discoverable.

Also, if there was a FAQ it could go in there. I’ve been collecting a list of gotchas that I see people commonly posting, and have been meaning to post to try to get this going.

2 Likes

It just needs to be clear that if you want a dynamic layout you need to pass app.layout a function i.e. app.layout = lambda: function(args) or app.layout = function. If you want static layout pass an object app.layout = function(args) or app.layout = function().

I really appreciate the discussion. I’ll look out for that common “gotchas” list @nedned.