Stop probing the remote command every poll #225

Closed
opened 2026-08-25 06:31:40 +01:00 by naps62-yolo · 1 comment
Owner

The health endpoint is polled every 15 seconds by the settings view. #200
probes the remote-command translation engine by running its configured
template over an empty batch and requiring a clean exit — which means the
template executes roughly 5,700 times a day purely to light a lamp.

The template is an arbitrary command; ssh box claude -p is the example
DESIGN.md §15 gives. Opening an SSH connection, or invoking an agent CLI,
every fifteen seconds is not a health check — it is a load generator, and on a
metered backend it is a bill.

The other lamps do not have this problem: they are cheap HTTP calls to
endpoints that cost no quota.

Options, roughly in order of preference:

  • Cache the command probe's result for several minutes and serve the cached
    verdict to the fast poll.
  • Probe it on a slower tick than the rest of the chain.
  • Probe it only when the operator opens /settings, or on demand from a
    button, rather than on every poll.

Whichever is chosen, the lamp should still go red promptly once a translation
actually fails — a real failure is better evidence than a synthetic probe, so
folding the last real outcome into the lamp is worth considering alongside.

Found while implementing #200.

Depends on: #193, #200

The health endpoint is polled every 15 seconds by the settings view. #200 probes the remote-command translation engine by running its configured template over an empty batch and requiring a clean exit — which means the template executes roughly **5,700 times a day** purely to light a lamp. The template is an arbitrary command; `ssh box claude -p` is the example DESIGN.md §15 gives. Opening an SSH connection, or invoking an agent CLI, every fifteen seconds is not a health check — it is a load generator, and on a metered backend it is a bill. The other lamps do not have this problem: they are cheap HTTP calls to endpoints that cost no quota. Options, roughly in order of preference: - Cache the command probe's result for several minutes and serve the cached verdict to the fast poll. - Probe it on a slower tick than the rest of the chain. - Probe it only when the operator opens `/settings`, or on demand from a button, rather than on every poll. Whichever is chosen, the lamp should still go red promptly once a translation actually fails — a real failure is better evidence than a synthetic probe, so folding the last real outcome into the lamp is worth considering alongside. Found while implementing #200. Depends on: #193, #200
naps62-yolo added this to the Subtitles milestone 2026-08-25 06:31:40 +01:00
naps62-yolo added the area/apidifficulty/easyphase/9-subtitlestype/bug labels 2026-08-25 06:31:40 +01:00
naps62-yolo added a new dependency 2026-08-25 06:31:40 +01:00
Author
Owner

Fixed the remote-command translation engine's health lamp by caching its probe verdict for 5 minutes (arr_subs::COMMAND_PROBE_CACHE_TTL), moved into Command itself (crates/arr-subs/src/command.rs) rather than the API layer, so the cache applies wherever Backend::probe is called, not just from /api/health. probe() now serves the cached verdict inside the TTL, and only spawns the template when the cache is empty or stale; a change of the selected engine (which probe_engine in arr-api keys on) still bypasses any stale cache from a different engine because the cache lives per Command instance. Backend::probe's existing doc contract ("cheap by contract") is now actually honoured by this backend instead of being violated by it. Added a unit test that back-dates the cache to prove TTL expiry, and an integration test proving a second call within the TTL does not re-run the command.

Branch: subtitles/225-lamp-cadence (based on blitz/subtitles).

just ci passed: 706/706 tests, clippy and fmt clean.

Findings

Did not implement the "fold the last real translation outcome into the lamp" idea the issue raises as worth considering alongside caching — that needs the daemon to feed real translate() results from the reconcile loop back into whatever the health endpoint reads, which is a bigger seam than exists today (no such channel from arr-daemon to arr-api's health probes). Left out as out of scope for this issue; worth its own issue if wanted.

Fixed the remote-command translation engine's health lamp by caching its probe verdict for 5 minutes (`arr_subs::COMMAND_PROBE_CACHE_TTL`), moved into `Command` itself (`crates/arr-subs/src/command.rs`) rather than the API layer, so the cache applies wherever `Backend::probe` is called, not just from `/api/health`. `probe()` now serves the cached verdict inside the TTL, and only spawns the template when the cache is empty or stale; a change of the selected engine (which `probe_engine` in `arr-api` keys on) still bypasses any stale cache from a different engine because the cache lives per `Command` instance. `Backend::probe`'s existing doc contract ("cheap by contract") is now actually honoured by this backend instead of being violated by it. Added a unit test that back-dates the cache to prove TTL expiry, and an integration test proving a second call within the TTL does not re-run the command. Branch: `subtitles/225-lamp-cadence` (based on `blitz/subtitles`). `just ci` passed: 706/706 tests, clippy and fmt clean. ## Findings Did not implement the "fold the last real translation outcome into the lamp" idea the issue raises as worth considering alongside caching — that needs the daemon to feed real `translate()` results from the reconcile loop back into whatever the health endpoint reads, which is a bigger seam than exists today (no such channel from `arr-daemon` to `arr-api`'s health probes). Left out as out of scope for this issue; worth its own issue if wanted.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Reference: yolo/arr#225