refactor: jit compilation (EXP)#6674
Conversation
Greptile SummaryThis PR introduces a dev-only JIT (just-in-time) page compilation mode (
Confidence Score: 3/5The JIT compilation path has a URL construction bug that silently breaks first-visit compilation whenever The stub's
Important Files Changed
Reviews (1): Last reviewed commit: "refactor: jit compilation" | Re-trigger Greptile |
| ' url.pathname = "/_jit/compile";\n' | ||
| f' url.search = "?route=" + encodeURIComponent({route_literal});\n' |
There was a problem hiding this comment.
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.
| 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) |
There was a problem hiding this comment.
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.
| 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}) |
There was a problem hiding this comment.
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.
All Submissions:
Type of change
Please delete options that are not relevant.
New Feature Submission:
Changes To Core Features:
After these steps, you're ready to open a pull request.