fix(runner): worker sluit af i.p.v. sleep infinity — zelfherstel na DB-hik #57

Merged
janpeter merged 1 commit from claude/worker-selfheal into master 2026-07-10 17:59:25 +02:00
Owner

Fixt de storing waar scrum4me-server:claude en ik allebei op stuitten: op 2026-07-09 (~20:50 UTC) parkeerden alle workers op beide hosts na een korte DB-onbereikbaarheid, en stonden 18 uur later nog stil. docker ps meldde de hele tijd "Up".

Oorzaak

bin/run-agent.sh deed op drie plekken sleep infinity als het opgaf. Het proces bleef leven, dus restart: always vuurde nooit (die reageert op een exit, niet op een marker-bestand), en zonder healthcheck was de dode worker onzichtbaar.

Bijkomend, en dit is het gat dat scrum4me-server:claude precies opmat op 154: de pre-flight heeft een retry-budget van ~5 min (AGENT_PREFLIGHT_RETRIES = 30×10s), maar de job-loop backofft maar ~75 s (5×, 5+10+20+40) vóór hij opgaf. Een DB-hik tussen 80 s en 5 min parkeerde de vloot terwijl een herstart hem had opgevangen. De juni-fix (#39) gaf de pre-flight dat budget; de job-loop kreeg het nooit.

De fix

Alle drie de sleep infinityexit:

plek was wordt
pre-flight-park sleep infinity exit "$preflight_rc"
token-expired sleep infinity exit 3
job-loop max-fail sleep infinity exit 1

De kern: entrypoint.sh doet exec gosu agent run-agent.sh, dus dit script is het hoofdproces. Bij exit sterft de container en grijpt restart: always. De herstart doorloopt opnieuw de pre-flight-lus, dus de job-loop erft dat 5-minuten-budget alsnog — via de restart, niet via een eigen teller. Een transiënte storing die binnen dat venster heelt lost zichzelf op; een echt kapotte worker crash-loopt zichtbaar (docker ps "Restarting", door Docker's restart-backoff gedempt) in plaats van een leugenachtige "Up".

Bewuste ruil

De health-server (child van entrypoint.sh) verdwijnt bij exit — precies de reden die het oude commentaar gaf om te parkeren. Die ruil is expliciet geaccepteerd: de run-logs onder /var/log/agent overleven de herstart en zijn de diagnosebron, en een zichtbare crash-loop is een eerlijker signaal dan een stil geparkeerd proces achter een valse "Up".

Buiten scope

  • Healthcheck op worker-idea. De repo-agent-service in docker-compose.yml heeft er al een; max2's host-local compose (/srv/scrum4me/compose/docker-compose.yml) niet. Met deze exit-fix is dat nu secundair: de valse "Up" is al weg, de restart is automatisch. Aparte host-compose-follow-up.
  • Fleet-rebuild/recreate. Deze PR wijzigt alleen de image-bron. Uitrollen is een aparte, zichtbare stap (redeploy_all_workers op max2; 154 rolt zelf uit).

Verificatie

bash -n schoon, geen actief sleep infinity meer (de 3 resterende treffers zijn toelichtende comments). Gedragsverificatie vergt een image-rebuild — dat gebeurt bij de uitrol, niet in deze PR.

Fixt de storing waar scrum4me-server:claude en ik allebei op stuitten: op 2026-07-09 (~20:50 UTC) parkeerden **alle** workers op **beide** hosts na een korte DB-onbereikbaarheid, en stonden 18 uur later nog stil. `docker ps` meldde de hele tijd "Up". ## Oorzaak `bin/run-agent.sh` deed op drie plekken `sleep infinity` als het opgaf. Het proces bleef leven, dus `restart: always` vuurde nooit (die reageert op een **exit**, niet op een marker-bestand), en zonder healthcheck was de dode worker onzichtbaar. Bijkomend, en dit is het gat dat scrum4me-server:claude precies opmat op 154: de **pre-flight** heeft een retry-budget van ~5 min (`AGENT_PREFLIGHT_RETRIES` = 30×10s), maar de **job-loop** backofft maar ~75 s (5×, 5+10+20+40) vóór hij opgaf. Een DB-hik tussen 80 s en 5 min parkeerde de vloot terwijl een herstart hem had opgevangen. De juni-fix (#39) gaf de pre-flight dat budget; de job-loop kreeg het nooit. ## De fix Alle drie de `sleep infinity` → `exit`: | plek | was | wordt | |---|---|---| | pre-flight-park | `sleep infinity` | `exit "$preflight_rc"` | | token-expired | `sleep infinity` | `exit 3` | | job-loop max-fail | `sleep infinity` | `exit 1` | De kern: `entrypoint.sh` doet `exec gosu agent run-agent.sh`, dus dit script is het hoofdproces. Bij `exit` sterft de container en grijpt `restart: always`. De herstart doorloopt **opnieuw de pre-flight-lus**, dus de job-loop erft dat 5-minuten-budget alsnog — via de restart, niet via een eigen teller. Een transiënte storing die binnen dat venster heelt lost zichzelf op; een echt kapotte worker crash-loopt zichtbaar (`docker ps` "Restarting", door Docker's restart-backoff gedempt) in plaats van een leugenachtige "Up". ## Bewuste ruil De health-server (child van `entrypoint.sh`) verdwijnt bij exit — precies de reden die het oude commentaar gaf om te parkeren. Die ruil is expliciet geaccepteerd: de run-logs onder `/var/log/agent` overleven de herstart en zijn de diagnosebron, en een zichtbare crash-loop is een eerlijker signaal dan een stil geparkeerd proces achter een valse "Up". ## Buiten scope - **Healthcheck op `worker-idea`.** De repo-`agent`-service in `docker-compose.yml` heeft er al een; max2's host-local compose (`/srv/scrum4me/compose/docker-compose.yml`) niet. Met deze `exit`-fix is dat nu secundair: de valse "Up" is al weg, de restart is automatisch. Aparte host-compose-follow-up. - **Fleet-rebuild/recreate.** Deze PR wijzigt alleen de image-bron. Uitrollen is een aparte, zichtbare stap (`redeploy_all_workers` op max2; 154 rolt zelf uit). ## Verificatie `bash -n` schoon, geen actief `sleep infinity` meer (de 3 resterende treffers zijn toelichtende comments). Gedragsverificatie vergt een image-rebuild — dat gebeurt bij de uitrol, niet in deze PR.
fix(runner): worker sluit af i.p.v. sleep infinity — zelfherstel na DB-hik
All checks were successful
CI / Compose config (pull_request) Successful in 4s
CI / Docker build (pull_request) Successful in 29s
6b75519515
Op 2026-07-09 (~20:50 UTC) parkeerden alle workers op beide hosts na een korte
DB-onbereikbaarheid, en stonden 18 uur later nog stil. `docker ps` meldde de hele
tijd "Up".

Oorzaak: `run-agent.sh` deed op drie plekken `sleep infinity` als het opgaf. Het
proces bleef daardoor leven, waardoor `restart: always` nooit vuurde — die reageert
op een exit, niet op een gemarkeerd bestand — en zonder healthcheck was de dode
worker onzichtbaar.

Bijkomend gat: de pre-flight heeft een retry-budget van ~5 min (30×10s,
`AGENT_PREFLIGHT_RETRIES`), maar de job-loop backofft maar ~75s (5×, 5+10+20+40)
vóór hij opgaf. Een DB-hik tussen 80s en 5 min parkeerde de vloot terwijl een
herstart hem had opgevangen.

Fix: alle drie de `sleep infinity` worden `exit`.

- pre-flight-park  → `exit "$preflight_rc"`
- token-expired    → `exit 3`
- job-loop max-fail → `exit 1`

Omdat de herstart opnieuw de pre-flight-lus doorloopt, erft de job-loop nu alsnog
dat 5-minuten-budget — via de restart, niet via een eigen teller. Een transiënte
storing die binnen dat venster heelt lost zichzelf op; een echt kapotte worker
crash-loopt zichtbaar (docker ps "Restarting", door Docker's restart-backoff
gedempt) in plaats van een leugenachtige "Up".

Bewuste ruil: de health-server (child van entrypoint) verdwijnt bij exit. De
run-logs onder /var/log/agent overleven de herstart en zijn de diagnosebron; een
zichtbare crash-loop is een eerlijker signaal dan een stil geparkeerd proces.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
janpeter merged commit 594413e2fc into master 2026-07-10 17:59:25 +02:00
Sign in to join this conversation.
No reviewers
No labels
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
janpeter/scrum4me-docker!57
No description provided.