Skip to content

job-scheduled-run: job timeouts are not enforced in the durable record — a timed-out run persists status:'success' #7734

Description

@huangyiirene

Symptom

A job declared with timeout: 2000 whose handler sleeps 10 s records sys_job_run.status: 'success' with duration_ms ~10000 (five times the declared timeout), and sys_job reads last_status: success, failure_count: 0. Expected per JobExecutionStatus / #3494: run status timeout, counted as a failure. The scheduler does abandon and retry (a retryPolicy variant fired attempt 2 at timeout+backoff), yet both attempts still landed as success rows. Relatedly, sys_job_run.attempt reads 1 for every retry.

Root cause

In packages/services/service-job/src/db-job-adapter.ts, schedule() (~line 80) computes const wrapped = this.wrap(name, handler, 'schedule') and passes wrapped into IntervalJobAdapter / CronJobAdapter. Those adapters apply the timeout guard via run-with-policy.ts withTimeoutPromise.race([run(), guard]) (~line 50) — so the guard races the recording wrapper, not the raw handler. When the guard rejects, wrap()'s inner await handler(ctx) (~line 200) is still pending on the un-cancellable handler; when it finally resolves, wrap() writes finishRun(runId, 'success', ...) (~line 211). The timeout status the adapters compute lives only in the in-memory JobExecution list that sys_job_run never reads — which is why interval-job-adapter.test.ts's in-memory "records status 'timeout'" assertion stays green while the persisted record an operator sees says success. The same seam explains the hardcoded attempt: 1 in startRun (~line 233).

Stale-premise check: db-job-adapter.ts, run-with-policy.ts, interval-job-adapter.ts, cron-job-adapter.ts all have zero diff between the 92f26f75 pin and origin/main — not fixed upstream. (Same timeout-guard-wins-race-but-recorder-still-resolves class as the closed #4875 / #4813 in HealthMonitor; distinct subsystem.)

Reproduction

  1. Declare a job with timeout: 2000 whose handler sleeps 10 s; let it fire.
  2. Read sys_job_run and sys_job.

Expected: run status timeout, sys_job.failure_count incremented. Actual: status: 'success', duration_ms ~10000, failure_count: 0; retries also persist as success and attempt stays 1.

Source

Extracted from the QA run #7690 (framework 92f26f7, console 09987b680).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions