fix: the boot poll could not detect the thing it exists to detect - #125
Merged
Conversation
Reported as "the preview loads but nothing happens when I click", which is exactly the state the disconnect banner was written to announce and did not. A preview machine idle-stops. Waking one takes about 36 seconds, measured: three requests to a cold preview answered 503 at 35.7s, 503 at 35.9s, and 200 at 36.8s. During that window the page has rendered, the socket underneath it is dead, and every click goes nowhere. Neither detector fired. The socket goes silent rather than closing, so no close event. And the /boot poll had no timeout — a stopped machine does not *refuse* a request, the host holds it open while the machine wakes, so the fetch neither succeeded nor threw. `misses` stayed at zero and the banner never showed. A server that is slow and a server that is gone are the same thing to someone clicking, and only the second was handled. The poll is bounded now, and bounded shorter than its own interval so the polls take turns rather than stacking. The test checks both, and reads the two numbers out of the page rather than trusting a comment: a timeout longer than the interval fails with "waits 5000ms but fires every 4000ms". Previews also get a 45s health-check grace period. Ten seconds meant the first probe ran against a machine that was still starting — opening SQLite and seeding the demo history — so a waking preview answered 503 to the person waiting on it rather than making them wait. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XjCx5QszLXroGYh1YBxybw
powderman previewhttps://powderman-pr-125.fly.dev The workbench, live: split/join/swap areas, the F3 palette, the widgets, themes, tooltips, the status bar. On branches with the MCP server, point an agent at it: claude mcp add --transport http powderman-preview "https://powderman-pr-125.fly.dev/mcp"Updated on every push. Destroyed when this PR closes. |
The Fly logs disprove the reasoning I gave for widening the health-check grace period: 18:01:57 machine started in 2.29s 18:02:00 demo mode: seeded 7 runs 18:02:00 Health check on port 8080 is now passing 18:02:00 machine became reachable in 2.52s Startup is ~3 seconds, seeding included. Ten seconds of grace was never too short, and 45 would not have helped anything. The ~36s I measured is Fly's proxy failing to wake a stopped machine — its own dashboard says "Proxy is having trouble reaching app" — which is on the other side of the wire from anything in this repo. Reverted. What is worth changing is the line above it. The daemon binds 0.0.0.0 and printed "powderman on http://localhost:8080", which is the exact string Fly Doctor pattern-matches to report "App is not listening to the expected port... make sure your app is listening on 0.0.0.0 and not localhost". A line meant to be helpful was arguing for a bug that was not there, in a console someone reads while debugging. It prints the bind address now. The boot-poll timeout in the previous commit stands on its own: a stopped machine holds the request rather than refusing it, so the detector could not fire, whatever the reason the wake is slow. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XjCx5QszLXroGYh1YBxybw
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reported as "the preview loads but nothing happens when I click".
The real bug
The
/bootpoll had no timeout:A stopped machine doesn't refuse the request — the host holds it open while waking. So the fetch neither succeeded nor threw,
catchnever ran,missesstayed at zero, and the disconnect banner never showed. The websocket goes silent rather than closing, so that detector missed it too.Both paths assumed failure looks like an error. Here it looks like patience — and a server that is slow and a server that is gone are the same thing to someone clicking.
Bounded, and shorter than its own 4s interval so polls take turns rather than stacking behind a hanging one. Two misses (~8s) shows "Server unreachable — waiting…"; the existing boot-id branch reloads once the machine is genuinely up.
The test reads both numbers out of the generated page rather than trusting the comment, and I checked it bites:
A correction, from the Fly logs
My first version of this PR also widened the preview health-check grace period from 10s to 45s, on the theory that a cold start was slow. The logs disprove that:
Startup is ~3 seconds, seeding included. Ten seconds of grace was never too short and 45 would have helped nothing. The ~36s wake is Fly's proxy failing to start a stopped machine — its own dashboard says "Proxy is having trouble reaching app" — which is the other side of the wire from this repo. That change is reverted.
The boot-poll fix stands on its own: whatever makes the wake slow, the detector couldn't fire.
One thing the logs did earn
The daemon binds
0.0.0.0and printedpowderman on http://localhost:8080. That's the exact string Fly Doctor pattern-matches to report "App is not listening to the expected port… make sure your app is listening on 0.0.0.0 and not localhost" — so a line meant to be helpful was arguing for a bug that wasn't there, in a console someone reads while debugging. It prints the bind address now.166 tests pass; fmt clean, clippy clean under
-D warnings.🤖 Generated with Claude Code
https://claude.ai/code/session_01XjCx5QszLXroGYh1YBxybw