Skip to main content

Documentation

No results found.
Features

Background Tasks

Most WebProCMS installs run background work inline — deferred closures after the response is sent, plus Lazy Cron for scheduled maintenance — because shared hosts rarely run a persistent queue:work process. But installs that do run a worker...

Most WebProCMS installs run background work inline — deferred closures after the response is sent, plus Lazy Cron for scheduled maintenance — because shared hosts rarely run a persistent queue:work process. But installs that do run a worker (a VPS with Supervisor, an owner-managed server) had no way to see it: is the worker alive, is the queue backing up, did anything fail? The Background Tasks screen (Dashboard → Develop → Background Tasks, admin and up) answers all three, and degrades gracefully on worker-less installs instead of alarming about a setup that's perfectly normal.


What the screen shows

Section What it tells you
Queue worker The active queue connection/driver and a liveness verdict: Active (heartbeat within 5 minutes), Stale (a worker ran before but has gone quiet), Not detected (never seen — the normal fleet state), or Sync driver (jobs run inline, nothing to monitor). Shows the last heartbeat time and the last job a worker processed.
Test queue worker A probe button (hidden on the sync driver): dispatches a trivial job onto the real queue and watches for a worker to pick it up. Confirms end-to-end liveness in seconds, or times out after 15 s — and then sweeps the probe job back off the database queue so worker-less installs don't accumulate junk rows.
Queue stats Pending / in-flight / failed counts and the oldest pending job's wait time, per the driver-agnostic size-inspection API (works on database and Redis queues). A warning callout appears when jobs are pending but no active worker is draining them.
Queued jobs The first 25 rows of the database queue: job class, queue name, age, attempts, waiting vs. processing.
Failed jobs The 25 most recent failed_jobs rows with the exception's first line. Per-row Retry (pushes it back onto the queue) and Delete, plus Retry all / Delete all (both deletes are modal-confirmed).
Long-running tasks Progress bars for any work that self-reports through BackgroundProgress (see below), plus an in-flight AI site-generation run.

The page live-refreshes every 10 seconds while open.

How worker detection works

There is no reliable "is a process running?" API on shared hosting (exec is usually disabled), so detection is heartbeat-based (QueueWorkerMonitor, registered in AppServiceProvider):

  • queue:work's loop event fires every poll cycle — even when idle — and stamps a cache timestamp (throttled to one write per 30 s).
  • Every real queued-job execution stamps it too, and records the job's class name.
  • The sync connection is deliberately ignored: sync jobs run inline in the dispatching request, and jobs run via defer() never fire queue events — so neither can fake a live worker on an install that has none.

The heartbeat lives in the cache (database-backed by default), so a CLI worker and the FPM dashboard share state, including across servers with a shared cache backend.

Self-reported progress (BackgroundProgress)

Queue tables only know pending/reserved/failed — a "63% done" bar requires the work to report its own progress. BackgroundProgress is the one-line-per-milestone registry for that:

BackgroundProgress::start('media-cloud-migration', 'Migrating media to cloud', total: $count);
BackgroundProgress::increment('media-cloud-migration');   // per item
BackgroundProgress::complete('media-cloud-migration');    // or ::fail($key, $error)

Entries live in cache and age out on their own (6 h after the last update), so a crashed job's bar disappears instead of sticking forever. Any job, deferred closure, or artisan command can adopt it incrementally.

Setup

Nothing to configure. Installs without a worker see an informational note (that's the supported default — Lazy Cron and deferred execution handle background work). To run a real worker, start php artisan queue:work under a process monitor such as Supervisor and set QUEUE_CONNECTION=database (or redis) in .env; the screen will show it as Active within seconds of its first poll.

What the heartbeat unlocks

Worker detection isn't just informational — it gates real dispatch decisions:

  • Lazy Cron queue offload (opt-in, Settings → Advanced → Lazy Cron): due scheduled tasks are handed to the worker instead of running inline, with automatic rescheduling if a dispatch is lost. See Lazy Cron → Queue worker offload.
  • Worker-optional job sites (AI site generation, theme switching) use QueueDispatch::dispatchOrDefer(): they dispatch to the queue only when a fresh heartbeat proves a worker is draining it, and otherwise run the work after the response via defer() — replacing the old "an async driver implies a worker" guess with evidence.

Limitations

  • Per-queue row listing covers the database driver; other drivers get aggregate counts for their default queue only.
  • Progress bars only appear for work that calls BackgroundProgress (or the AI site generator, which has its own run state) — adoption across long-running jobs is incremental.
  • Scheduled maintenance tasks are intentionally not duplicated here — they live on the Lazy Cron panel (Dashboard → Settings → Advanced), which the screen links to.