Reflex: cached @rx.var or on_load event handler for keeping deployed page data fresh?
0 reputation · 01 Apr 2024, 23:52 UTC
0 reputation · 01 Apr 2024, 23:52 UTC
In a Reflex app I am designing, several pages display values derived from a database. I can either expose them as @rx.var computed vars on the state, or fetch them explicitly in an on_load event handler and store the result in plain state attributes.
My understanding is that a computed var caches its value and only recomputes when a state attribute it reads changes, so it would never notice new rows written by another process. An on_load handler re-runs on each page load, guaranteeing freshness but repeating the query every time. Reflex does not appear to offer a built-in TTL or invalidation policy for computed vars, so the freshness-versus-performance trade-off seems to be left entirely to the developer.
Assume a recent Reflex release (exact version to be confirmed with pip show reflex, since caching semantics have shifted between versions) and a production deployment, not dev mode.
For a field where staleness of a few minutes is acceptable but silently serving pre-deploy data is not:
on_load handler a reliable hybrid, or are there edge cases where the var still serves a stale cached value?For a page that tolerates a few‑minute staleness but must not serve pre‑deployment data, the safest pattern is to fetch the data in an on_load handler and store it in ordinary state attributes. A computed @rx.var that depends on those attributes will then recompute on every page load, ensuring the displayed value reflects the latest database state. A pure @rx.var that runs a query inside its body will never notice writes made by other processes unless the query touches a state attribute that changes, because the cache is keyed only on tracked attribute reads.
In practice, the hybrid approach works reliably, provided you keep the following edge cases in mind:
on_load handler does not fire again, so the computed var will continue to display the value from the initial load. To keep data fresh during a session you need an explicit refresh mechanism (e.g., a periodic on_interval event that re‑runs the on_load logic or calls a dedicated refresh handler).Reflex does not expose a TTL or explicit invalidation API for @rx.var. The only supported way to force a recomputation is to mutate a state attribute that the var reads. A common pattern is to maintain a last_refreshed timestamp attribute that you update whenever you fetch fresh data; the computed var then depends on that timestamp, ensuring it re‑runs whenever you update it.
State lifetimes and worker counts differ between reflex run (dev mode) and a production build (reflex build && reflex run --prod). Dev mode typically runs a single worker with hot‑reload enabled, so you may see stale data persist longer than in production where multiple workers serve separate connections. Always test the staleness behaviour in an environment that matches your production worker count.
Determine the exact Reflex version:
pip show reflex
Create a test page that uses an on_load handler to query a count from your database and store it in a state attribute row_count. Then add a computed var:
@rx.var
async def row_count_display(self):
return f"Rows: {self.row_count}"
Deploy the page, load it, and record the displayed count.
From a separate process, insert a new row into the table.
Reload the page and confirm the count has increased. If it has not, the computed var is still using stale data and you need to adjust the fetch logic.
Repeat the test in a production‑style build (matching worker count) to ensure behaviour is consistent.
Optionally, add a periodic on_interval event that re‑runs the on_load logic to keep data fresh during long sessions.
Could you confirm whether your production deployment uses a single Reflex worker or multiple workers? This detail can affect how state is shared across clients and may influence the staleness you observe.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.