Skip to content

Bound the /__hot-reload__ long-poll duration#1395

Open
richard-to wants to merge 2 commits into
mainfrom
claude/hotreload-prod-access-a5bfrz
Open

Bound the /__hot-reload__ long-poll duration#1395
richard-to wants to merge 2 commits into
mainfrom
claude/hotreload-prod-access-a5bfrz

Conversation

@richard-to

@richard-to richard-to commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • The /__hot-reload__ debug endpoint blocked its request indefinitely until a code change triggered a reload. Combined with Flask's dev server processing one request at a time by default, a single in-flight poll could serialize (or in the worst case, freeze) every other request against the app for as long as that connection stayed open.
  • Cap the poll at a max duration (_MAX_POLL_SECONDS = 25 in mesop/server/server_debug_routes.py) so no single request can hold a connection open indefinitely. If the counter hasn't changed by the deadline, the endpoint returns the unchanged counter instead of continuing to block.
  • Update the client (mesop/web/src/services/channel.ts) to only trigger a hot reload when the returned counter actually differs from what it last saw, and to otherwise immediately re-poll. This keeps behavior identical for real reload events while making the timeout-and-retry path a no-op rather than a spurious reload.

Not included

  • An earlier version of this PR also added threaded=True to Mesop's built-in dev-server .run() calls (CLI, Colab/notebook, and the mesop pip binary) as a complementary fix. That's been reverted for now: it surfaces a separate, unrelated latent race in MemoryStateSessionBackend (unsynchronized check-then-act on a shared in-memory dict) that needs its own fix first. Tracking that as a follow-up rather than bundling it here.

Test plan

  • Verified against a running Flask test client (with _MAX_POLL_SECONDS temporarily shrunk) that an unchanged counter times out and returns the same value after the bounded duration, and that a counter bump mid-poll still returns promptly with the new value.
  • CI

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01SH7W9VinxHv8yMEwfW1yXX

claude added 2 commits July 6, 2026 16:23
The /__hot-reload__ debug endpoint blocked a request thread indefinitely
until a code change triggered a reload, and Flask's dev server ran
unthreaded, so a single in-flight poll serialized all other requests.
Cap the poll at a max duration (client already retries) and run the
dev server threaded so one slow/long connection can't starve everyone
else.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH7W9VinxHv8yMEwfW1yXX
The threaded dev server change surfaces an unrelated latent race in
MemoryStateSessionBackend (unsynchronized check-then-act on a shared
dict) that needs its own fix first. Splitting this out as a separate
improvement; the bounded hot-reload poll stands on its own.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH7W9VinxHv8yMEwfW1yXX
@richard-to richard-to changed the title Bound hot-reload long-poll and enable threaded dev server Bound the /__hot-reload__ long-poll duration Jul 7, 2026
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.

2 participants