Skip to content

fix(endpoint): slow the health polls down instead of stopping them - #3911

Open
ntdatt812 wants to merge 1 commit into
decolua:masterfrom
ntdatt812:fix/endpoint-slow-poll-when-healthy
Open

ntdatt812 wants to merge 1 commit into
decolua:masterfrom
ntdatt812:fix/endpoint-slow-poll-when-healthy

Conversation

@ntdatt812

Copy link
Copy Markdown
Contributor

The endpoint page states its polling design in a comment:

// Adaptive: slow when healthy, fast when degraded; pause when tab hidden.

Two of the three are implemented. "slow when healthy" is not. Both effects return before an interval is ever created once everything is reachable:

if (allHealthy) return () => document.removeEventListener("visibilitychange", onVisible);
const timer = setInterval(..., STATUS_POLL_FAST_MS);
if (tunnelHealthy && tsHealthy) return;
const id = setInterval(probeBoth, CLIENT_PING_FAST_MS);

So the page does not slow down when healthy — it stops. STATUS_POLL_SLOW_MS and CLIENT_PING_SLOW_MS have been sitting in endpointConstants.js beside their FAST siblings, never imported. The design was written down twice, in a comment and in a pair of constants, and wired neither time.

Why it matters more than a stale constant

It inverts what the browser-side probe is for. Its own comment:

probes tunnel/tailscale URLs directly so UI stays "reachable" even when backend DNS (1.1.1.1) hiccups on *.ts.net or *.trycloudflare.com

It switches off exactly when the UI is green — which is the state in which nothing else is watching. The effects only re-run when their dependencies change, and their dependencies are the health flags they would have been the ones to update. On a page left open, a tunnel that drops keeps reading as reachable until the user changes tabs and visibilitychange fires.

The change

Pick the interval from health rather than bailing out. The hidden-tab guards are untouched — probeBoth still returns immediately on document.hidden, and the status poll still checks it — so a background tab does no work at either speed. The miss-debounce (REACHABLE_MISS_THRESHOLD, 5 consecutive misses) is untouched too, so the slow path takes five minutes to flip a connection red rather than flipping on one blip.

I have deliberately not changed either constant's value; 30s and 60s are what the file already chose for this.

Verification

npx next build — 0 errors.

npx vitest@3 run --config tests/vitest.config.js tests/unit/endpoint-adaptive-poll.test.js
→ 6 passed

Reverting EndpointPageClient.js fails 3 of the 6 and nothing else.

The tests are source-level assertions, which needs saying plainly: the page is a client component and this repo has no React renderer in its test setup, so I cannot drive the timers. A test that only asserted the SLOW constants exist would have passed against the bug — that is precisely how it survived — so these assert the call sites instead, including that the early returns are gone and that the hidden-tab guards are still there.

This is a behaviour change, not just a tidy-up, so it is worth being explicit: it adds one request every 30s (status) and one every 60s (client ping) to a healthy, visible endpoint page that previously made none. If stopping when healthy was the intent and the comment is what is wrong, say so and I will send the one-line comment fix and delete the two constants instead.

The effect above the browser-side probe describes the design:

  // Adaptive: slow when healthy, fast when degraded; pause when tab hidden.

Two of the three are implemented. "slow when healthy" is not: both the
tunnel status poll and the client ping return before creating any interval
once everything is reachable, so the page stops polling rather than slowing
down. STATUS_POLL_SLOW_MS and CLIENT_PING_SLOW_MS have been sitting unused
beside their FAST siblings.

That inverts what the client ping is for. Its own comment says it probes
the URLs directly so the UI stays honest "even when backend DNS hiccups" --
and it switched off exactly when the UI was green, which is the state in
which nothing else would notice a tunnel dropping underneath it. Until the
user changed tabs, a dead tunnel kept reading as reachable.

Pick the interval from health instead of bailing out. The hidden-tab guards
are untouched, so a background tab still does no work.
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.

1 participant