Background jobs
What it is
ManpowerIQ uses Hangfire for exactly one thing: running the Excel employee-import as a fire-and-forget background job so a large upload doesn't block the HTTP request. There are no scheduled or recurring jobs in v1 — no cron-style sweeps, no timed dispatch of any kind.
Both halves of that sentence matter, so this page states them plainly:
- Fire-and-forget import job — built and live.
- Scheduled/recurring jobs — none exist.
Why it's built this way
Bulk employee import parses three spreadsheet sheets and upserts many rows; doing that inline would tie up the request and risk timeouts. So import is enqueued as an async Hangfire job that transitions through a tracked lifecycle, while the API returns immediately with a job record the UI can poll (sheet 03 §how-it-works, §decisions). That single use case is the only thing that needed a background worker — so Hangfire was wired for it and nothing else.
The jobs a reader might expect from the handover (certification-expiry sweep, notification dispatch) were not built as jobs — see Gotchas.
How it works
sequenceDiagram
participant U as User
participant C as ImportsController
participant H as Hangfire queue "imports"
participant R as ImportJobRunner
participant DB as PostgreSQL
U->>C: upload .xlsx
C->>C: synchronous pre-flight (size/ext/schema)
C->>DB: create import_jobs row (Queued)
C->>H: enqueue job
C-->>U: 201 + job record
H->>R: run [Queue("imports")]
R->>DB: Queued → Running → Completed / Failed
U->>C: poll job status / download error report
- The import job is a real async pipeline on the Hangfire queue
imports, not inline and not a stub —ImportJobRunnercarries the[Queue("imports")]attribute and drives the job fromQueued → Running → Completed(sheet 03 §build-status + §decisions,ImportJobRunner.cs:18,ImportsController.cs:143). - Pre-flight validation is synchronous (non-empty, ≤10 MB,
.xlsx, schema check) and returns 400 before anything is enqueued (sheet 03 §rules,ImportsController.cs:67-102). - The job runs without an HTTP context, so it sets a manual tenant override and the Postgres RLS GUC itself; it skips the connection initializer under the EF InMemory provider (sheet 03 edge-cases,
ImportJobRunner.cs:33-51). - Lifecycle states:
Queued → Running → (Completed | CompletedWithErrors | Failed); a user cancel (SuperAdmin +imports.view_history) moves it toCancelledand the worker checks the cancel token cooperatively between rows (sheet 03 §rules).
Gotchas / constraints
- No recurring/scheduled jobs. Verified: grep for Hangfire
RecurringJob→ no matches (the only hits were EFValueGeneratedOnAddOrUpdatefalse positives). Don't document a cron-style scheduler — there isn't one. - Certification expiry has no auto-sweep. Certs do not transition Active→Expired on a timer; there is no background job for it (sheet 04 §build-status). Any "expired" state is computed/visible only where the read logic does so, not swept by a job.
- Notifications have no backend job. The pending-count bell is a frontend poll every 30s against live counts; there is no backend notification system — no sender, template, dispatcher, or entity (sheet 18 §build-status). It is not a Hangfire job.
- The import job is provider-sensitive. Because it sets the tenant/RLS GUC manually and branches on the provider, it can behave differently under EF InMemory vs Postgres — cover it with a Postgres integration test, not just InMemory unit tests (sheet 03 edge-cases).
- Uploaded file is in-memory only (never written to disk), SHA-256 hashed; a duplicate within 24h warns but does not block (sheet 03 §rules).
Build status
- Available — the Hangfire fire-and-forget Excel import job (
ImportJobRunner, queueimports), end-to-end with a job table and error-report regeneration (sheet 03 §build-status). - Planned / absent — any scheduled or recurring job: certification-expiry sweep (sheet 04), backend notification dispatch (sheet 18). No scheduled jobs in v1.
Related
- Multi-tenancy — how the job sets the tenant GUC without an HTTP context.
- Fact sheets: 03 (employee master — import), 04 (skills & certifications), 18 (notifications).