Skip to content

feat(core): whole-job deadline + scrub driver-exception messages from run error (rec #5) - #230

Merged
thiagoluga merged 1 commit into
masterfrom
feat/whole-job-deadline
Jul 30, 2026
Merged

feat(core): whole-job deadline + scrub driver-exception messages from run error (rec #5)#230
thiagoluga merged 1 commit into
masterfrom
feat/whole-job-deadline

Conversation

@thiagoluga

Copy link
Copy Markdown
Owner

Fifth maintainer-decision recommendation — two enterprise-readiness items.

A) Whole-job deadline

Complements WP6's per-attempt read timeout. ReportBuilder<T>.Deadline(TimeSpan) sets an overall wall-clock bound on the entire run — reads, writes and uploads together — so a report that never hangs on any single step but drags on overall is still stopped. RunAsync applies it via a linked CancellationTokenSource + CancelAfter; on expiry the run is cooperatively cancelled (surfaces as a cancelled run) and a warning distinguishes a deadline expiry from a caller-requested cancel. Off by default. Applied on both production entry paths (the job worker and the sync endpoint both call RunAsync; nothing in production calls ExecuteAsync directly).

B) Error scrub at the source (the WP5 async-path follow-up)

The run's persisted error (job.Error, surfaced by GET /jobs), the RunFailed and Retry job events, and the sync-run error previously carried the raw exception message — for a driver exception, that can echo the connection string (host/port/database/username). Now:

  • NeoReports' own exceptions keep their curated, secret-free message (e.g. No source named 'sales-db').
  • Any other exception is reduced to its type name (SqlException, SocketException, …).
  • The full exception is still logged through ILogger for diagnosis (WP7).

Applied consistently at every site that persists a failure reason: AbortStrategy, the runner's read-failure and retry paths, and the job worker's catch (which handles exceptions that escape the runner during setup — the completeness gap review found). This closes the async-path leak WP5's endpoint scrub had left, without genericizing the whole job timeline (the maintainer-judgment concern from WP5): the timeline still shows a useful reason, just secret-free.

Tests

Deadline cancels a slow run promptly (100ms deadline stops a 5s-per-page source) and a no-deadline run completes; the driver message is gone from the run error on both the abort and skip-read-failure paths, while a NeoReports message survives.

Verification

  • dotnet build --no-incremental — 0 warnings, 0 errors.
  • Core unit suite 296/296; Jobs 32/32.
  • Combined correctness + security review: deadline correctness (CTS disposal, the catch filter fires only on true expiry, both entry paths covered), scrub consistency across all sites, and the SourceRef curated-message survival all confirmed. The worker-catch gap and stale endpoint comment it flagged are fixed in this PR; the earlier async-scrub follow-up task is dismissed as resolved.

Recs #1#4 merged (#226#229). This is #5. Last up: the minor items (#6) — CA1068 CancellationToken ordering, CI fail-not-skip for Docker-absent integration tests, XLSX number-format assertions (already added in #226), and the drain-helper page caps.

… the run error

Two enterprise-readiness items from the audit.

Whole-job deadline (complements WP6's per-attempt read timeout): ReportBuilder<T>.
Deadline(TimeSpan) sets an overall wall-clock bound on the entire run — reads,
writes and uploads together — so a report that never hangs on a single step but
drags on overall is still stopped. RunAsync applies it via a linked
CancellationTokenSource + CancelAfter; on expiry the run is cooperatively
cancelled (surfaces as a cancelled run) and a warning distinguishes a deadline
expiry from a caller-requested cancel. Off by default. Applied on both production
entry paths (the job worker and the sync endpoint both call RunAsync).

Error scrub at the source: the run's persisted error (job.Error, surfaced by
GET /jobs), the RunFailed and Retry job events, and the sync-run error previously
carried the raw exception message — for a driver exception that can echo the
connection string (host/port/database/username). Now NeoReports' own exceptions
keep their curated, secret-free message (e.g. "No source named 'x'") and any
other exception is reduced to its type name; the full exception is still logged
through ILogger for diagnosis. Applied consistently at every site that persists a
failure reason: AbortStrategy, the runner's read-failure/retry paths, and the job
worker's catch (which handles exceptions that escape the runner during setup).
This closes the async-path leak the WP5 endpoint scrub had left, without
genericizing the whole job timeline.

Tests: deadline cancels a slow run promptly and a no-deadline run completes; the
driver message is gone from the run error (abort and skip-read-failure paths)
while a NeoReports message survives.
@sonarqubecloud

Copy link
Copy Markdown

@sonarqubecloud

Copy link
Copy Markdown

@thiagoluga
thiagoluga merged commit ef3aa28 into master Jul 30, 2026
5 checks passed
@thiagoluga
thiagoluga deleted the feat/whole-job-deadline branch July 30, 2026 14:51
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.

1 participant