You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Freshness signal: refresh cached data only when the source actually changes #17141
Let a cached data source declare a cheap "has the source changed?" probe, so
Streamlit re-runs the expensive query (and updates dependent fragments) only
when the underlying data actually changed — instead of on a blind timer. This
is the event-driven counterpart to timer-based refresh.
Why?
Timer-based refresh (@st.fragment(run_every=…), or a server-side scheduled
refresh) fires on a fixed clock regardless of whether anything changed. For an
expensive query that forces a bad tradeoff:
Poll fast → the heavy query re-runs every interval even when the data is
identical, wasting warehouse/API cost.
Poll slow → users stare at stale data between ticks.
Sources usually expose something cheap that reveals whether they changed — a
version token, an ETag, a MAX(updated_at), a Snowflake stream or
change-tracking timestamp. There's no built-in way to poll that cheap signal
often and re-run the expensive work only when it moves.
How?
Attach a cheap freshness function to a cached data source. Streamlit checks the
freshness token; when it changes, it invalidates the cache and re-runs the
fragments that read it. When it's unchanged, the cached value is served and no
expensive work runs.
Sketch (names illustrative):
deforders_version():
# Cheap probe — cache invalidates when this value changes.returnconn.query("SELECT MAX(updated_at) FROM orders")
@st.cache_data(freshness=orders_version)defload_orders():
returnconn.query("SELECT * FROM orders") # expensive; runs only on change@st.fragment(run_every="5s")deforders_panel():
df=load_orders() # cheap probe each poll; heavy query only when changedst.line_chart(df, x="ts", y="orders")
orders_panel()
This is an enhancement request for a source-change freshness probe on @st.cache_data. A cheap callable (a version token, ETag, or MAX(updated_at)) would be checked on access; when its value changes, Streamlit would drop the cached result, rerun the expensive function, and refresh fragments that read it. @st.cache_data currently keys entries by hashed arguments and expires them with ttl, .clear(), or, when refresh_mode="background", a time-based stale window in DataCache._is_stale (lib/streamlit/runtime/caching/cache_data_api.py). The nearest lifecycle hook is st.cache_resource(validate=...), which receives the cached object and returns a boolean, and which is only on st.cache_resource. Callers can approximate the sketch by polling a token inside @st.fragment(run_every=...) and passing that token as a cache argument; that stores a new entry per token and still depends on each session's timer. A built-in probe that invalidates the existing entry and wakes readers is absent from the cache API.
Related Issues
#17142 — Companion proposal from the same author for a shared, server-side refresh_every schedule. It points here as the event-driven counterpart to that timer. The two issues cover different triggers and should stay separate.
#14357 — Asks for st.cache_resource(validate=...) to receive the cached function's arguments so a cheap external check (file mtime) can invalidate an expensive load, and notes the same hook may belong on st.cache_data. That overlaps the invalidation use case; the existing hook still validates the stored object and leaves fragment reruns to the caller.
#17009 — Requests a max_stale window for refresh_mode="background". That is the same cache-freshness area, with elapsed time as the trigger and a configurable grace period for serving the previous value.
This is an automated triage comment using grok-4.7-xhigh. Please verify the suggested labels and related issues.
Activity
github-actions commentedon Sep 26, 2026
To help Streamlit prioritize this feature, react with a 👍 (thumbs up emoji) to the initial post.
Your vote helps us identify which enhancements matter most to our users.
github-actions commentedon Oct 20, 2026
This is an enhancement request for a source-change freshness probe on
@st.cache_data. A cheap callable (a version token, ETag, orMAX(updated_at)) would be checked on access; when its value changes, Streamlit would drop the cached result, rerun the expensive function, and refresh fragments that read it.@st.cache_datacurrently keys entries by hashed arguments and expires them withttl,.clear(), or, whenrefresh_mode="background", a time-based stale window inDataCache._is_stale(lib/streamlit/runtime/caching/cache_data_api.py). The nearest lifecycle hook isst.cache_resource(validate=...), which receives the cached object and returns a boolean, and which is only onst.cache_resource. Callers can approximate the sketch by polling a token inside@st.fragment(run_every=...)and passing that token as a cache argument; that stores a new entry per token and still depends on each session's timer. A built-in probe that invalidates the existing entry and wakes readers is absent from the cache API.Related Issues
refresh_everyschedule. It points here as the event-driven counterpart to that timer. The two issues cover different triggers and should stay separate.st.cache_resource(validate=...)to receive the cached function's arguments so a cheap external check (file mtime) can invalidate an expensive load, and notes the same hook may belong onst.cache_data. That overlaps the invalidation use case; the existing hook still validates the stored object and leaves fragment reruns to the caller.max_stalewindow forrefresh_mode="background". That is the same cache-freshness area, with elapsed time as the trigger and a configurable grace period for serving the previous value.This is an automated triage comment using
grok-4.7-xhigh. Please verify the suggested labels and related issues.