Skip to content

refactor: jit compilation (EXP)#6674

Draft
FarhanAliRaza wants to merge 1 commit into
reflex-dev:mainfrom
FarhanAliRaza:jit-compile
Draft

refactor: jit compilation (EXP)#6674
FarhanAliRaza wants to merge 1 commit into
reflex-dev:mainfrom
FarhanAliRaza:jit-compile

Conversation

@FarhanAliRaza

Copy link
Copy Markdown
Contributor

All Submissions:

  • Have you followed the guidelines stated in CONTRIBUTING.md file?
  • Have you checked to ensure there aren't any other open Pull Requests for the desired changed?

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

New Feature Submission:

  • Does your submission pass the tests?
  • Have you linted your code locally prior to submission?

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

After these steps, you're ready to open a pull request.

a. Give a descriptive title to your PR.

b. Describe your changes.

c. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if such).

@FarhanAliRaza FarhanAliRaza requested a review from a team as a code owner June 23, 2026 17:36
@FarhanAliRaza FarhanAliRaza changed the title refactor: jit compilation refactor: jit compilation (EXP) Jun 23, 2026
@greptile-apps

greptile-apps Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces a dev-only JIT (just-in-time) page compilation mode (REFLEX_JIT=true) that speeds up boot time by compiling only stateful pages eagerly and serving lightweight stubs for all other routes; the stubs trigger real compilation on first browser visit via a new /_jit/compile endpoint. It also refactors create_passthrough_component_memo to evaluate the memo body only once, calling _lift_rest_props before the tag hash is computed.

  • JIT compiler bootstrap: On startup compile_app reads the existing stateful_pages.json marker to decide which routes to defer; deferred routes get a JSX stub that fetches /_jit/compile?route=... on mount and reloads when the real module is ready.
  • compile_route_jit: Compiles a single page on demand, writes the JSX and memo files, and updates in-memory page/stateful-page state; the /_jit/compile Starlette endpoint wraps this in asyncio.to_thread.
  • Memo refactor: create_passthrough_component_memo now calls _lift_rest_props before computing the tag hash and constructs MemoComponentDefinition directly instead of going through _create_component_definition and then patching the result.

Confidence Score: 3/5

The JIT compilation path has a URL construction bug that silently breaks first-visit compilation whenever backend_path is configured, and the feature lacks concurrency protection for simultaneous page requests.

The stub's url.pathname = "/_jit/compile" unconditionally replaces the full path on the URL derived from env.PING, which means any non-default backend_path (e.g. /api) causes every JIT compile request to 404 — the route never loads beyond its stub. The marker is also never flushed after an on-demand compile, so stateful status gained via JIT isn't preserved across restarts.

reflex/compiler/compiler.py — the _jit_stub_code function's pathname assignment and compile_route_jit's missing marker flush both need attention before this is used in any non-default configuration.

Important Files Changed

Filename Overview
reflex/compiler/compiler.py Adds JIT stub code generation and compile_route_jit; the stub's hardcoded pathname breaks when backend_path is configured, and the function doesn't persist updated stateful-page metadata after compiling
reflex/app.py Adds the /_jit/compile GET endpoint and jit_compile closure; endpoint is guarded by is_prod_mode(), but the handler has no concurrency control for parallel compile requests
packages/reflex-base/src/reflex_base/environment.py Adds REFLEX_JIT environment variable with a False default and a clear "Never set in prod" note
packages/reflex-base/src/reflex_base/components/memo.py Refactors create_passthrough_component_memo to evaluate the memo body only once, moving the _lift_rest_props call before tag computation and building MemoComponentDefinition directly

Reviews (1): Last reviewed commit: "refactor: jit compilation" | Re-trigger Greptile

Comment on lines +1007 to +1008
' url.pathname = "/_jit/compile";\n'
f' url.search = "?route=" + encodeURIComponent({route_literal});\n'

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.

P1 Hardcoded pathname strips backend_path prefix

The stub overwrites url.pathname with the literal "/_jit/compile", which discards any configured backend_path prefix. When a user sets backend_path="/api" in their Reflex config, the server registers the endpoint at /api/_jit/compile (via config.prepend_backend_path("/_jit/compile")), but the stub always fetches /_jit/compile — a 404 on every first-visit compile attempt. The fix is to derive the backend path prefix from the PING URL rather than hardcoding the pathname:

url.pathname = url.pathname.substring(0, url.pathname.lastIndexOf('/')) + "/_jit/compile";

This preserves /api (or any other prefix) while replacing only the endpoint segment.

Comment on lines +1417 to +1420
for compiled_route, page_ctx in compile_ctx.compiled_pages.items():
if isinstance(page_ctx.root_component, Component):
app._pages[compiled_route] = page_ctx.root_component
app._stateful_pages.update(compile_ctx.stateful_routes)

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.

P2 Stateful pages marker not updated after JIT compile

compile_route_jit updates app._stateful_pages but never calls app._write_stateful_pages_marker(). If the JIT-compiled route turns out to be stateful, that information is never persisted to the stateful_pages.json marker on disk. On the next process restart the route won't appear in the marker, so it will be deferred again instead of being compiled at boot time — meaning every restart incurs another first-request compilation overhead for stateful routes that have already been seen.

Comment thread reflex/app.py
Comment on lines +1541 to +1551
async def _jit_compile(request: Request) -> Response:
route = request.query_params.get("route")
if route is None:
return JSONResponse({"error": "missing route"}, status_code=400)
from reflex.compiler import compiler

try:
await asyncio.to_thread(compiler.compile_route_jit, app, route)
except ValueError:
return JSONResponse({"error": f"unknown route {route!r}"}, status_code=404)
return JSONResponse({"compiled": route})

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.

P2 No concurrency guard on simultaneous JIT compile requests

Multiple browser tabs (or a quick refresh) can trigger concurrent GET requests for the same deferred route, each of which calls asyncio.to_thread(compiler.compile_route_jit, app, route). These run in parallel threads with no lock protecting the shared app._unevaluated_pages, app._pages, or app._stateful_pages dicts, nor the file writes. In the worst case two threads race to write the same .jsx file, or one reads stale in-progress state from the other. A simple asyncio.Lock keyed by route (or a global compile lock) would prevent duplicate concurrent compiles.

@FarhanAliRaza FarhanAliRaza marked this pull request as draft June 23, 2026 17:42
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.

1 participant