Skip to content

Keep at most one fallback frontend poll in flight - #89

Merged
alexanderoster merged 2 commits into
alexanderoster:developfrom
krixkrix:frontend-poll-gate
Oct 23, 2026
Merged

alexanderoster merged 2 commits into
alexanderoster:developfrom
krixkrix:frontend-poll-gate

Conversation

@krixkrix

Copy link
Copy Markdown

When the event stream is unavailable, the client polls /api/frontend every 600 ms, even if the previous request hasn't returned. On a server slower than that, the requests pile up: against one answering in 3 s, five were in flight at once and the queued ones hit the 10 s request timeout.

A poll tick is now skipped while an /api/frontend request is still pending, so the interval stretches to the server's response time. A hung request holds polling back for at most the existing 10 s request timeout. Navigation refreshes are not held back.

A skipped tick also skips updateModules, so while polling, parameter lists, logs and similar data refresh once per reply instead of every 600 ms.

The change is in Client/core, so it covers both the Vue and Svelte clients.

NB: PR #88 dependency:
The second commit adds node:test tests in Client/tests/ (no npm dependencies; run npm test there). They need AMCListDeltaSync.js from #88, so merge this after #88. If you don't want a JS test setup in the repo, the first commit works on its own.

When the event stream is unavailable, the client polls /api/frontend
every 600 ms. Each tick sent a request whether or not the previous one
had returned, so a server slower than the interval got an ever growing
number of overlapping requests: more load on a server that is already
behind. In a browser against a server answering polls after 3 s, five
polls were in flight at once and the queued ones ran into the 10 s
request timeout, because they also took the browser's connections. The
reply serials only dropped out-of-order results; the requests still
went out.

A tick is now skipped while a request sent by retrieveFrontendState is
pending (frontendRequestsInFlight), so a poll tick never adds a second
request and the effective interval stretches to the server's response
time. The counter is released however the request ends; the default
10 s timeout of axiosGetRequest bounds how long a hung request holds
polling back. Navigation refreshes are not held back: the new page's
values are needed at once, and the existing serials keep an older poll
reply from overwriting them. A poll still pending when the stream takes
over is dropped as before but keeps the gate until it settles, also if
the client falls back to polling again in the meantime.

Each tick also calls updateModules, which is what refreshes module data
such as parameter lists and logs while polling. A skipped tick skips
that too, so with a slow /api/frontend those modules are refreshed once
per reply rather than every 600 ms, even if their own endpoints are
fast. A server that slow is likely slow for them as well.

The other periodic client requests (parameter list, logs, configuration
list, part list, execution list deltas) already allow one request at a
time.

The change is in Client/core, so it applies to both the Vue and the
Svelte client.
Client/core is framework-agnostic ES module code, so it can be tested
under plain node without a browser or the client's npm packages.
Client/tests is a small harness for that: support/coreLoader.mjs imports
core modules through loader hooks (support/coreLoaderHooks.mjs) that
load core's .js files as ES modules and replace the third-party imports
(axios, luxon, three) with stubs. There are no dependencies; run
"npm test" in Client/tests, or "node --test 'Client/tests/*.test.mjs'"
from the repository root.

frontendPollGate.test.mjs covers the gate from the previous commit: its
release on reply, error and timeout, a server slower than the interval,
a navigation refresh during a pending poll, and switching to the stream
and back.

Loading AMCApplication.js imports the list modules, which need
Client/core/common/AMCListDeltaSync.js, so the tests require that file.
@alexanderoster
alexanderoster merged commit eff0932 into alexanderoster:develop Oct 23, 2026
2 checks passed
@krixkrix
krixkrix deleted the frontend-poll-gate branch October 23, 2026 19:50
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