Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions mesop/server/server_debug_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,20 @@
from mesop.runtime import runtime
from mesop.server.server_utils import prefix_base_url

# Cap how long a single poll can block so that a request can't tie up a
# server thread/connection indefinitely when no hot reload ever happens
# during its lifetime. The client detects an unchanged counter and simply
# polls again.
_MAX_POLL_SECONDS = 25


def configure_debug_routes(flask_app: Flask):
@flask_app.route(prefix_base_url("/__hot-reload__"))
def hot_reload() -> Response:
counter = int(request.args["counter"])
while True:
if counter < runtime().hot_reload_counter:
start_time = time.monotonic()
while counter >= runtime().hot_reload_counter:
if time.monotonic() - start_time > _MAX_POLL_SECONDS:
break
# Sleep a short duration but not too short that we hog up excessive CPU.
time.sleep(0.1)
Expand Down
10 changes: 8 additions & 2 deletions mesop/web/src/services/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -562,9 +562,15 @@ export class Channel {
);
if (response.status === 200) {
const text = await response.text();
this.hotReloadCounter = Number(text);
this.hotReload();
const newCounter = Number(text);
this.hotReloadBackoffCounter = 0;
// The server may respond without the counter having changed (e.g.
// it hit its max poll duration with no reload having happened).
// Only trigger a reload when the counter actually advanced.
if (newCounter !== this.hotReloadCounter) {
this.hotReloadCounter = newCounter;
this.hotReload();
}
// Use void to explicitly not await to avoid downstream sync issue.
void pollHotReloadEndpoint();
} else {
Expand Down
Loading