Errands: with the worker down, an errand page says the work carries on and polls every two seconds for up to a day #1122

Closed
opened 2026-10-31 03:04:10 +00:00 by tiagoagueda · 0 comments
Owner

What exists today

src/postulo/core/views_errands.py:26 sets POLL_SECONDS = 2; templates/core/partials/errand_state.html puts hx-trigger="every 2s" on every unfinished errand and says "You can leave this page. The work carries on without you."

An errand left waiting or working is only failed by core/errands.py:193-208 (fail_interrupted, default hours=24, keyed on created_at), run from the scheduler pass (send_due_reminders.py:214). When the worker container is stopped, crashed, or was never started on an instance with POSTULO_BACKGROUND_WORK on, a tab left open polls 1,800 times an hour and tells the person the work is carrying on, for up to 24 hours. A worker killed mid-task (#474's case) is the same.

The instance already knows when the worker last went round: the heartbeat file (core/scheduler.py:111-118, worker_last_pass), which the worker's own healthcheck treats as dead after 5 minutes (docker/compose.yml:111) and the staff overview shows.

What would change

  1. In state_context, when the errand is unfinished and scheduler.worker_last_pass() is missing or older than five minutes, add worker_silent=True. The fragment then says, in place of "carries on without you", that the background worker has not been seen for a while and the work will start when it is back (one new translatable sentence), and polls every 30 s instead of 2.
  2. In fail_interrupted, fail a working errand whose started_at is older than a bound tied to the longest handler (an hour, say) rather than 24 hours from creation; keep 24 hours for waiting ones, which a returning worker will still run.

No library needed. Tests: a fragment for a waiting errand with a stale heartbeat carries the new sentence and a 30 s trigger; with a fresh heartbeat it is as today; a working errand started two hours ago is failed by the pass.

What it saves or protects

small: an open errand tab stops making 1,800 requests an hour against a worker that is not there, and the person is told the truth within minutes instead of after 24 hours

Accessibility

must be checked: the state line stays inside the page's existing live region (errand.html, #500) so the new sentence is announced; the no-script Check again link stays; the errand page tests and the browser test of the errand dialog (#674) must stay green

Decisions

  • How long may an errand be working before it is called interrupted? Recommended: one hour from started_at; captures and renders have their own timeouts well under that.

Found by the efficiency and reliability audit of 2026-10-30 (finding B6; index postulo/postulo#1108). Checked against main at daee30b15 (2026-10-30).

## What exists today `src/postulo/core/views_errands.py:26` sets `POLL_SECONDS = 2`; `templates/core/partials/errand_state.html` puts `hx-trigger="every 2s"` on every unfinished errand and says "You can leave this page. The work carries on without you." An errand left *waiting* or *working* is only failed by `core/errands.py:193-208` (`fail_interrupted`, default `hours=24`, keyed on `created_at`), run from the scheduler pass (`send_due_reminders.py:214`). When the worker container is stopped, crashed, or was never started on an instance with `POSTULO_BACKGROUND_WORK` on, a tab left open polls 1,800 times an hour and tells the person the work is carrying on, for up to 24 hours. A worker killed mid-task (#474's case) is the same. The instance already knows when the worker last went round: the heartbeat file (`core/scheduler.py:111-118`, `worker_last_pass`), which the worker's own healthcheck treats as dead after 5 minutes (`docker/compose.yml:111`) and the staff overview shows. ## What would change 1. In `state_context`, when the errand is unfinished and `scheduler.worker_last_pass()` is missing or older than five minutes, add `worker_silent=True`. The fragment then says, in place of "carries on without you", that the background worker has not been seen for a while and the work will start when it is back (one new translatable sentence), and polls every 30 s instead of 2. 2. In `fail_interrupted`, fail a *working* errand whose `started_at` is older than a bound tied to the longest handler (an hour, say) rather than 24 hours from creation; keep 24 hours for *waiting* ones, which a returning worker will still run. No library needed. Tests: a fragment for a waiting errand with a stale heartbeat carries the new sentence and a 30 s trigger; with a fresh heartbeat it is as today; a working errand started two hours ago is failed by the pass. ## What it saves or protects small: an open errand tab stops making 1,800 requests an hour against a worker that is not there, and the person is told the truth within minutes instead of after 24 hours ## Accessibility must be checked: the state line stays inside the page's existing live region (`errand.html`, #500) so the new sentence is announced; the no-script *Check again* link stays; the errand page tests and the browser test of the errand dialog (#674) must stay green ## Decisions - **How long may an errand be *working* before it is called interrupted?** Recommended: one hour from `started_at`; captures and renders have their own timeouts well under that. --- Found by the efficiency and reliability audit of 2026-10-30 (finding B6; index postulo/postulo#1108). Checked against `main` at `daee30b15` (2026-10-30).
tiagoagueda added this to the 0.6.0 milestone 2026-10-31 03:04:10 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#1122
No description provided.