Skip to content

Single-pass compiler: Remove legacy multi-threading/multiprocessing executor infrastructure #6217

@masenf

Description

@masenf

Summary

Remove the ExecutorSafeFunctions class, ExecutorType environment configuration, and all concurrent.futures-based parallelism from the compilation pipeline. Replace with straightforward sequential execution.

This is a cleanup task that can be done as part of ENG-9144 or independently. It's broken out separately because the executor removal is self-contained and can be reviewed independently of the larger plugin integration.

Background

The current compiler has vestiges of a complex parallelization strategy:

ExecutorSafeFunctions (in reflex/compiler/compiler.py)

A helper class that stashes component data in class-level attributes so it can be accessed by forked child processes (since the data isn't picklable). Methods:

  • compile_page(route) — compiles a page from the stashed data
  • compile_unevaluated_page(route, style, theme) — evaluates and compiles a page
  • compile_theme(style) — compiles the theme

ExecutorType (in reflex/environment.py)

An enum that selects between ThreadPoolExecutor and ProcessPoolExecutor based on an environment variable (REFLEX_COMPILE_EXECUTOR).

Usage in App._compile() (in reflex/app.py)

executor = ExecutorType.get_executor_from_environment()

for route, component in zip(self._pages, page_components, strict=True):
    ExecutorSafeFunctions.COMPONENTS[route] = component

with executor as executor:
    result_futures = []
    for route in self._pages:
        f = executor.submit(ExecutorSafeFunctions.compile_page, route)
        result_futures.append(f)
    # ... submit stylesheet and theme compilation too
    for future in concurrent.futures.as_completed(result_futures):
        ...

Why this should be removed

As stated in the project brief: "The compiler is largely CPU bound and doesn't benefit from python threads due to the GIL and multiprocessing incurs significant enough overhead that it doesn't improve compile times for most apps."

The parallelism adds significant complexity:

  • ExecutorSafeFunctions with its class-level data stashing is a workaround for unpicklable data
  • Process pool executor requires forking, which has its own issues (copy-on-write memory, module reimports)
  • Thread pool executor is limited by the GIL for CPU-bound work
  • The progress bar tracking has to work across process boundaries
  • Error handling across futures is more complex

What to Do

Remove

  • ExecutorSafeFunctions class in reflex/compiler/compiler.py
  • ExecutorType enum in reflex/environment.py (or just the compile executor variant)
  • REFLEX_COMPILE_EXECUTOR environment variable handling
  • All concurrent.futures imports and usage in App._compile()
  • The result_futures / as_completed pattern

Replace with

Simple sequential calls:

compile_results = []
for route in self._pages:
    compile_results.append(compiler.compile_page(route, page_components[route]))
    progress.advance(task)

compile_results.append(compiler.compile_root_stylesheet(self.stylesheets, self.reset_style))
compile_results.append(compiler.compile_theme(self.style))

Keep

  • The progress bar advancement (just call it directly instead of via callbacks)
  • Plugin pre_compile hooks (these use a different mechanism)
  • compile_theme, compile_page, compile_root_stylesheet functions themselves — just call them directly

Acceptance Criteria

  • ExecutorSafeFunctions class is deleted
  • No concurrent.futures usage remains in the compilation pipeline
  • REFLEX_COMPILE_EXECUTOR env var is no longer referenced
  • All tests pass
  • Compilation still works correctly for both dev and prod modes
  • No measurable performance regression (sequential should be equivalent or faster due to removed overhead)

Key Files

  • reflex/compiler/compiler.pyExecutorSafeFunctions class
  • reflex/app.pyApp._compile(), executor usage
  • reflex/environment.pyExecutorType enum

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementAnything you want improved

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions