Skip to content

fix(vars): use monotonic time for computed var intervals - #6768

Open
anxkhn wants to merge 4 commits into
reflex-dev:mainfrom
anxkhn:fix/computed-var-monotonic-interval
Open

fix(vars): use monotonic time for computed var intervals#6768
anxkhn wants to merge 4 commits into
reflex-dev:mainfrom
anxkhn:fix/computed-var-monotonic-interval

Conversation

@anxkhn

@anxkhn anxkhn commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Cached computed vars used datetime.now() to decide when an update interval had
elapsed. Wall-clock adjustments could therefore refresh a value early or keep it
cached longer than requested.

This change:

  • measures update intervals with time.monotonic() for synchronous and
    asynchronous computed vars
  • tags each reading with a process-local clock identifier, expiring legacy or
    cross-process persisted timestamps instead of comparing incompatible clocks
  • adds regression coverage for forward and backward wall-clock changes and
    incompatible persisted timestamp formats

Because monotonic clocks cannot be compared across processes, an interval-cached
value restored in another worker now recomputes on first access. Subsequent interval
checks use elapsed time within that process.

Testing

  • uv run pytest tests/units/vars/test_base.py -q (18 passed)
  • uv run pytest tests/units --cov --no-cov-on-fail --cov-report= (6630 passed,
    17 skipped, 78.07% coverage)
  • uv run ruff check .
  • uv run ruff format --check .
  • uv run pyright reflex tests (0 errors)
  • uv run towncrier check --config pyproject.toml --dir packages/reflex-base --compare-with origin/main

The regression tests fail against the previous implementation and pass with this
change.

@anxkhn
anxkhn requested a review from a team as a code owner July 15, 2026 04:48
@codspeed-hq

codspeed-hq Bot commented Jul 15, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 26 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing anxkhn:fix/computed-var-monotonic-interval (e4123be) with main (a8857cd)

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a correctness bug in cached computed vars where datetime.now() was used to measure update intervals, making cache expiry sensitive to system clock adjustments (NTP slew, DST, manual changes). The fix replaces the interval check with time.monotonic() tagged with a process-local UUID, and falls back to the wall-clock datetime for state deserialized from another process or an older worker.

  • _mark_updated now writes both a datetime.datetime (for backward-compatible cross-process fallback) and a (_MONOTONIC_CLOCK_ID, time.monotonic()) tuple (for wall-clock-immune within-process checks).
  • needs_update preferentially uses the monotonic reading when its UUID matches the current process, and falls back to the wall-clock datetime otherwise, treating any non-datetime value as expired.
  • Five regression tests verify forward/backward clock changes, legacy datetime persistence, cross-process wall-clock fallback, and incompatible persisted timestamp formats.

Confidence Score: 5/5

Safe to merge. The change is narrowly scoped to interval expiry logic, is backward-compatible with older workers via the retained wall-clock attribute, and is fully covered by targeted regression tests.

The monotonic-clock logic is correct: UUID tagging cleanly isolates per-process readings, the wall-clock fallback handles cross-process and legacy state, and isinstance guards prevent crashes on unexpected attribute types. All affected code paths are exercised by the new tests, which were confirmed to fail against the old implementation.

Files Needing Attention: No files require special attention.

Important Files Changed

Filename Overview
packages/reflex-base/src/reflex_base/vars/base.py Replaces datetime.now() with time.monotonic() for interval checks; adds UUID-tagged monotonic attribute and wall-clock fallback for cross-process compatibility. Logic is correct and backward-compatible.
tests/units/vars/test_base.py Adds five targeted regression tests covering monotonic expiry, legacy datetime persistence, wall-clock fallback, cross-instance caching, and incompatible timestamp formats. Good coverage of the new code paths.
packages/reflex-base/news/6768.bugfix.md Changelog entry describing the fix accurately.

Reviews (4): Last reviewed commit: "test(vars): cover cross-instance cache r..." | Re-trigger Greptile

Comment thread tests/units/vars/test_base.py
Comment thread packages/reflex-base/src/reflex_base/vars/base.py Outdated
@Alek99 Alek99 closed this Jul 15, 2026
@Alek99 Alek99 reopened this Jul 15, 2026
anxkhn added 2 commits July 27, 2026 18:26
Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
@anxkhn
anxkhn force-pushed the fix/computed-var-monotonic-interval branch from 9339a6c to 98b18e3 Compare July 27, 2026 14:39
Store the wall-clock datetime under the legacy last-updated attribute so
older workers sharing a Redis state store never see an unreadable tuple,
and record the monotonic reading in a separate companion attribute tagged
with a per-process clock id. Interval checks use the monotonic reading
when it came from this process and fall back to the wall-clock comparison
otherwise, instead of forcing a recompute.

Also rename the news fragment to match the PR number (6768, not 6740).
Sequential requests landing on different workers must not force
recomputation of a fresh interval var; a foreign clock id degrades to
the wall-clock comparison, preserving cross-instance caching in Redis.

@FarhanAliRaza FarhanAliRaza left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change actually breaks rolling deployment as old worker can still use old datetime based time for computed vars.
Need better solution. Or atleast we know we have considered different solutions.

@FarhanAliRaza

FarhanAliRaza commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

@masenf

we probably don't need to save the last updated marker for vars that don't define _update_interval either

@masenf masenf left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

holding off on this one due to the potential for over-recomputing when a client bounces between different instances. probably we will take it for correctness eventually, it doesn't seem like something that happens a lot in the real world.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants