#17009·streamlit

Add `max_stale` parameter to `refresh_mode="background"` for configurable stale grace window

Author: sfc-gh-lwilby-1Created Sep 17, 2026Updated Sep 17, 2026
Labelstype:enhancementfeature:cache

Checklist

  • I have searched the existing issues for similar feature requests.
  • I added a descriptive title and summary to this issue.

Summary

When using refresh_mode="background" on st.cache_data / st.cache_resource, the stale grace window is fixed at 1×TTL (hard expiry at 2×TTL). This means an app idle overnight (longer than 2×TTL) has its cache evicted and the first morning user blocks on a cold miss.

A max_stale parameter would let users configure the stale grace window independently from TTL:

@st.cache_data(ttl="2m", max_stale="12h", refresh_mode="background")
def load_data():
    return expensive_query()

This separates two concerns: how often to refresh (ttl="2m") from how long stale data is acceptable (max_stale="12h"). Users who want frequent freshness checks but never want to block get exactly that.

Why?

From #11050 (comment by @itsToggle):

Is there any way that we can influence the "2 × ttl" background cache timeout? In my use case I want a short ttl so the values are updated often, but I never want my users to have to wait for a response. I would like to set the ttl to something like 2 minutes, but the oldest background cache that's allowed to be served can be much older than four minutes.

This is also the more practical solution for the "first user of the day" problem compared to cron scheduling, since it does not require an always-on server and works on scale-to-zero deployments.

How?

The background refresh spec already calls this out as future work:

max_stale parameter: Explicitly configure the stale grace window (default: equal to ttl), e.g. ttl="1h", max_stale="12h" to serve stale data across an overnight idle gap without blocking the first morning user

Additional Context