Skip to content

First lazy-init request blocks the entire API server (health checks included) until model loading finishes #1281

Description

@glb99

Did you check DeepWiki?

  • No, this is a new bug not covered in the docs

Describe the bug
When the REST API server is started with lazy model loading (ACESTEP_INIT_SERVICE=false, the default), the first request that triggers model initialization blocks the entire process — including GET /health and POST /query_result for unrelated jobs — for the full duration of model loading (DiT model load, LM tokenizer load, constrained-decoding setup, and especially vLLM's torch.compile warm-up on first LM use). In our case this exceeded 90+ seconds and made the server appear completely unreachable to a downstream client, even though it was working correctly the whole time and the job eventually succeeded.

Root cause: in acestep/api/job_runtime_state.py, ensure_models_initialized calls do_model_initialization(...) as a plain synchronous call inside async def, with no await/run_in_executor/asyncio.to_thread. Since this coroutine runs on the single-threaded asyncio event loop, that call blocks the whole event loop — and therefore every other in-flight request — until it returns.

async def ensure_models_initialized(app_state: Any) -> None:
    ...
    async with app_state._init_lock:
        ...
        do_model_initialization(app=_AppProxy(app_state), **init_kwargs)  # <- blocks the event loop

By contrast, the actual generation step in acestep/api/job_execution_runtime.py gets this right, using the same ThreadPoolExecutor already sitting on app_state:

loop = asyncio.get_running_loop()
result = await loop.run_in_executor(executor, _blocking_generate)

To Reproduce
Steps to reproduce the behavior:

  1. Start acestep-api (or the Docker image) with lazy init left at its default (ACESTEP_INIT_SERVICE=false).
  2. Submit a POST /release_task request with thinking: true on a fresh process (nothing loaded yet).
  3. Immediately poll GET /health or POST /query_result from a second client while the first request is being processed.
  4. Observe: these calls do not return until model initialization (DiT + LM + vLLM compile) finishes — often 60–120+ seconds on an 8GB-tier GPU — instead of returning promptly with a "still loading"/"queued" status.

Expected behavior
GET /health and POST /query_result should stay responsive during model initialization, the same way they already do once a job is running. A client should be able to tell "still initializing" apart from "server unreachable" without an indefinite/unbounded wait on a single HTTP call.

Proposed fix
Wrap the do_model_initialization(...) call in ensure_models_initialized with the same loop.run_in_executor(executor, ...) pattern already used for generation. Flagging this as an issue rather than jumping straight to a PR since it touches a code path shared across CUDA/CPU/MPS/XPU — want to check whether there's a reason it's currently synchronous (e.g. a backend/thread-safety constraint specific to model init) before assuming run_in_executor is safe everywhere. Happy to submit the PR once that's confirmed.

Screenshots
N/A — this is a backend/API timing issue, not a UI issue.

Desktop (please complete the following information):

  • OS: Windows 11 (Docker Desktop, WSL2 backend)
  • Deployment: ghcr.io/ace-step/ace-step-1.5:latest (Docker), CUDA backend
  • GPU: RTX 4060 Laptop, 8GB (tier3), acestep-v15-turbo + acestep-5Hz-lm-0.6B

Smartphone (please complete the following information):
N/A

Additional context
Confirmed via process inspection during the hang that the worker process stayed in a normal running state the whole time (not deadlocked/crashed) — this is a scheduling/blocking issue, not a crash. torch._inductor compile-worker subprocesses were actively spinning during the slow window, consistent with vLLM's first-use torch.compile cost. Not yet reproduced/checked on CPU/MPS/XPU backends.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions