Skip to content

fix: hold the server start lock in EvoSci deploy and the WebUI launcher - #661

Merged
X-iZhang merged 5 commits into
EvoScientist:mainfrom
Thanatos9404:fix/658-start-lock
Oct 19, 2026
Merged

X-iZhang merged 5 commits into
EvoScientist:mainfrom
Thanatos9404:fix/658-start-lock

Conversation

@Thanatos9404

@Thanatos9404 Thanatos9404 commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Description

Closes #658.

The human EvoSci deploy and the WebUI launcher now hold the server start lock (RUNTIME.lock_file) across the backend decision and the start, as ensure_langgraph_dev() and deploy --json already do. A second starter for the same port now waits for the first backend instead of killing it as stale or overwriting its pid file and ownership record.

  • deploy waits up to _FILE_LOCK_TIMEOUT (120 s) with a spinner, then refuses the running server as it does today; past that it exits 1 and names the lock file. Most of the server.py diff is steps 4 to 7 moving one level in, so hiding whitespace helps.
  • The launcher waits in 0.5 s slices, checking for stop() between them and once more with the lock held, so a stop while it waits starts nothing. A lock held too long is backend_start_failed with fields={"lock_file": ...}. The front-end preflight stays outside the lock.

The new tests in test_cli_deploy.py and test_launcher.py (reusing _LockHolder from test_cli_deploy_json.py) fail on main and pass here. Both modules now use the runtime_paths fixture, so the lock their tests take stays under tmp_path.

Type of change

  • Bug fix
  • New feature
  • Documentation / examples
  • Test improvement
  • Refactor (no behavior change)

Checklist

  • I have read the Contributing Guidelines
  • This targets core functionality used by the majority of users (niche features belong in EvoSkills)
  • I have added/updated tests where applicable
  • uv run ruff check . passes
  • uv run pytest passes (on Windows, 13 symlink-privilege and git-config tests fail the same way on main)

Summary by CodeRabbit

  • Bug Fixes
    • Improved backend and server startup when multiple EvoSci processes launch at the same time. A process now waits for another startup to finish before checking or starting the backend, reducing startup conflicts.
    • Startup stops cleanly if cancelled while waiting. If another process holds the startup lock beyond the wait limit, startup reports a failure instead of proceeding.
  • User Experience
    • The startup indicator now shows when EvoSci is waiting for another process to finish starting.

The TUI's ensure_langgraph_dev() and deploy --json hold RUNTIME.lock_file
while they decide whether to start a backend and while they start it. The
human deploy and the WebUI launcher did not, so a second starter for the
same port could kill a backend that was bound but not answering yet as
stale, or overwrite its pid file and ownership record.

deploy now takes the lock across its port check and the start, waiting up
to _FILE_LOCK_TIMEOUT with a spinner. The launcher takes it across the
backend decision and the start, waiting in 0.5 s slices that check for a
stop(), so a stop while it waits starts nothing; a lock held too long is
backend_start_failed with the lock file in fields.

Closes EvoScientist#658
The launcher checked for a stop() only before each wait for the start
lock, so a stop() in the slice in which the lock was freed still let the
decision and the start run. Check once more with the lock held.

Name the lock file in the launcher's error detail, word the deploy
messages as the rest of deploy does, and keep the deploy and launcher
tests' start lock, pid file and sidecar under tmp_path.
@coderabbitai

coderabbitai Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: afeb9574-fded-4a4e-a11a-a11542f8eb9d
📥 Commits

Reviewing files that changed from the base of the PR and between d20f195 and e37d6ca.

📒 Files selected for processing: 6
  • EvoScientist/deploy/launcher.py
  • EvoScientist/deploy/server.py
  • EvoScientist/deploy/webui.py
  • tests/test_cli_deploy.py
  • tests/test_launcher.py
  • tests/test_webui_launcher.py
+------------------+
| Zero bugs given. |
+------------------+
  \
   \   (\__/)
       (•ㅅ•)
       /   づ

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e776428a-9804-47fd-b6d2-e21ee0f55311
📥 Commits

Reviewing files that changed from the base of the PR and between c3637c2 and d20f195.

📒 Files selected for processing: 6
  • EvoScientist/deploy/launcher.py
  • EvoScientist/deploy/server.py
  • EvoScientist/deploy/webui.py
  • tests/test_cli_deploy.py
  • tests/test_launcher.py
  • tests/test_webui_launcher.py

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 6 remain after this review.

📝 Walkthrough

Human EvoSci deploy and the WebUI launcher now acquire the shared server-start lock before checking backend state and hold it through startup. Both paths handle lock timeouts and filesystem errors. The launcher also checks for cancellation while waiting and updates the startup status during lock waits.

Changes: Backend start locking

Layer / File(s) Summary
Human deploy lock and startup flow
EvoScientist/deploy/server.py, tests/test_cli_deploy.py
Human deploy holds the shared lock during the port check and server startup. Tests cover lock ordering, timeout, and filesystem errors.
Launcher backend decision and cancellation
EvoScientist/deploy/launcher.py, EvoScientist/deploy/webui.py, tests/test_launcher.py, tests/test_webui_launcher.py
The launcher resolves and starts the backend under the shared lock. It checks for cancellation while waiting and reports wait-state changes to the WebUI. Tests cover waiting, cancellation, timeout, and lock errors.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant deploy as Human deploy
  participant launcher as WebUI launcher
  participant lock as Shared start lock
  participant backend as Backend startup
  deploy->>lock: Acquire lock before port check
  lock-->>deploy: Grant lock or report timeout
  deploy->>backend: Run startup flow
  launcher->>lock: Acquire lock before backend decision
  lock-->>launcher: Grant lock, timeout, or cancellation
  launcher->>backend: Resolve and start backend when required
Loading

Suggested reviewers: x-izhang, jfilipiuk

Merge Risk: ⚪ Minimal · up to d20f1

The backend start lock is held until the server answers, so no actionable merge-blocking risk remains after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 40 functions across 6 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: holding the server-start lock during EvoSci deploy and WebUI launcher startup.
Linked Issues check Passed Issue #658 requires the human deploy command and WebUI launcher to hold RUNTIME.lock_file across backend decision and startup. deploy/server.py now wraps the port check and startup in `_start_lo…
Out of Scope Changes check Passed The production changes in server.py, launcher.py, and webui.py implement issue #658. The changes in test_cli_deploy.py, test_launcher.py, and test_webui_launcher.py verify the lock, timeou…
Full details: Linked Issues check

Explanation

Issue #658 requires the human deploy command and WebUI launcher to hold RUNTIME.lock_file across backend decision and startup. deploy/server.py now wraps the port check and startup in _start_lock(), waits up to _FILE_LOCK_TIMEOUT, and reports timeout or filesystem errors. deploy/launcher.py now holds the same lock across _resolve_backend() and _start_backend(). It checks stop() during 0.5-second waits and after lock acquisition. The supplied tests cover lock ordering, timeout, cancellation, callback reporting, and filesystem errors.

Full details: Out of Scope Changes check

Explanation

The production changes in server.py, launcher.py, and webui.py implement issue #658. The changes in test_cli_deploy.py, test_launcher.py, and test_webui_launcher.py verify the lock, timeout, cancellation, and wait-status behavior. The runtime-path fixture isolates the shared lock for these tests. No unrelated production behavior or test scope is shown.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@din0s din0s added bug Something isn't working external contribution Implementation from an external contributor labels Oct 19, 2026
@X-iZhang
X-iZhang requested a review from jfilipiuk October 19, 2026 13:17

@jfilipiuk jfilipiuk 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.

Approved. Two non-blocking nits on my side

self._warnings.append(hint)
if decision.action == "start":
self._start_backend()
with self._start_lock():

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.

Nit: while this waits up to 120 s for another process's start lock, the terminal shows the Starting langgraph dev... spinner from run_webui (webui.py), while deploy shows Waiting for another EvoSci process to start langgraph dev... for the same wait. A progress callback or a status update before the wait would tell the user they are blocked on another process.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in d20f195: WebUILauncher takes an on_lock_wait callback (True when start() has to wait for the lock, False once it holds it), and run_webui uses it to show Waiting for another EvoSci process to start langgraph dev... on its spinner, as deploy does, then goes back to Starting langgraph dev....

Comment thread EvoScientist/deploy/launcher.py Outdated

from ..langgraph_dev.manager import _FILE_LOCK_TIMEOUT, RUNTIME

RUNTIME.pid_dir.mkdir(parents=True, exist_ok=True)

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.

Nit: this mkdir (and the lock acquire) now runs outside _start_backend's except Exception, so on a read-only or full config dir the OSError leaves start() as is and run_webui (which catches only LauncherError) shows a traceback instead of the WebUI failed panel. Same in deploy (server.py _start_lock), which before reached the langgraph dev startup failed message. Wrapping it as backend_start_failed would keep the old behaviour.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in d20f195: an OSError from the mkdir or the lock acquire is now backend_start_failed with the old langgraph dev startup failed: ... message, so run_webui shows the WebUI failed panel, and deploy prints its startup failed message and exits 1. New tests cover both, for the mkdir and the acquire.

An OSError from the start lock's mkdir or acquire (a read-only or full
config dir) escaped the launcher's start() and deploy's _start_lock
unhandled, so run_webui showed a traceback instead of the WebUI failed
panel and deploy no longer reached its langgraph dev startup failed
message. The launcher now raises it as backend_start_failed and deploy
prints that message, as both did before the lock.

While another process held the lock, run_webui kept showing Starting
langgraph dev. WebUILauncher takes an on_lock_wait callback, called with
True when start() has to wait and then with False once it holds the
lock, and run_webui shows the waiting message deploy uses on its
spinner.

@X-iZhang X-iZhang left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for following up on #658 and on both of jfilipiuk's points. Three test suggestions inline in tests/test_launcher.py and tests/test_cli_deploy.py.

Comment thread tests/test_launcher.py Resolved
Comment thread tests/test_launcher.py Resolved
Comment thread tests/test_cli_deploy.py Resolved
@X-iZhang

X-iZhang commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Applied the suggestions and updated the branch; nothing more needed on your side.

@X-iZhang
X-iZhang enabled auto-merge (squash) October 19, 2026 16:43
@X-iZhang
X-iZhang merged commit 7fcde38 into EvoScientist:main Oct 19, 2026
9 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working external contribution Implementation from an external contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Human EvoSci deploy and the WebUI launcher start the backend without the server start lock

4 participants