Queuety

Introduction

Queuety is a WordPress plugin that provides a fast job queue and durable workflow engine. Workers process jobs from a minimal PHP bootstrap without booting WordPress, connecting to MySQL directly via PDO for maximum throughput.

Why Queuety?

WordPress has no real background job system. wp_cron only fires on page visits. Action Scheduler boots the entire WordPress stack for every batch. An LLM API call that takes 60 seconds gets killed by PHP's 30-second timeout. There's no way to run multi-step processes that survive crashes and resume where they left off.

Queuety solves this with two primitives:

  1. Jobs for fire-and-forget background work
  2. Workflows for durable multi-step processes with persistent state

Quick example

Dispatch a job using the modern API:

use Queuety\Contracts\Job;
use Queuety\Dispatchable;

readonly class SendEmailJob implements Job {
    use Dispatchable;

    public function __construct(
        public string $to,
        public string $subject,
        public string $body,
    ) {}

    public function handle(): void {
        wp_mail( $this->to, $this->subject, $this->body );
    }
}

SendEmailJob::dispatch( 'user@example.com', 'Welcome', 'Hello from Queuety!' );

Or use the classic handler name approach:

use Queuety\Queuety;

Queuety::dispatch( 'send_email', [ 'to' => 'user@example.com' ] );

Run a durable workflow:

Queuety::workflow( 'generate_report' )
    ->then( FetchDataHandler::class )
    ->then( CallLLMHandler::class )
    ->then( FormatOutputHandler::class )
    ->dispatch( [ 'user_id' => 42 ] );

Start a worker:

wp queuety work

Key differentiators

  • Fast execution. Workers skip the WordPress boot, connecting to MySQL directly via PDO. ~5ms overhead per batch vs ~200ms when loading WordPress.
  • Durable workflows. Multi-step processes with persistent state that survive PHP timeouts, crashes, and retries. Each step boundary is an atomic MySQL transaction.
  • Modern dispatch API. Self-contained readonly job classes with the Dispatchable trait, typed constructor properties as payload, and $tries/$timeout/$backoff declared on the class.
  • Middleware pipeline. Onion-style middleware for rate limiting, exception throttling, unique jobs, and custom logic. Declared per-job via a middleware() method.
  • Streaming steps. The StreamingStep interface yields chunks that are persisted to the database immediately. On retry, previously saved chunks are provided so the stream can resume where it left off. Ideal for LLM responses and large downloads.
  • Pluggable cache layer. CacheFactory auto-detects APCu or falls back to an in-memory cache. Custom backends implement Contracts\Cache. Used internally for rate-limiter lookups and queue state.
  • Batching and chaining. Dispatch groups of jobs with then/catch/finally callbacks and progress tracking, or chain jobs sequentially where each depends on the previous one completing.
  • Durable timers and signals. sleep() adds process-restart-safe delays. wait_for_signal() pauses a workflow until an external event arrives.
  • Heartbeats. Long-running steps send heartbeats to prevent the stale-job recovery system from reclaiming active work.
  • Workflow event log. Every step transition is recorded with a full state snapshot, enabling time-travel debugging via workflow_state_at().
  • Parallel steps. Run steps concurrently and wait for all to complete before advancing.
  • Conditional branching. Skip to named steps based on prior state using _goto.
  • Sub-workflows. Spawn child workflows that feed results back to the parent.
  • Priority queues. Four levels (Low, Normal, High, Urgent) via type-safe PHP enums.
  • Multi-queue workers. Process multiple queues with weighted priority ordering in a single worker.
  • Rate limiting. Per-handler execution limits with sliding window.
  • Recurring jobs. Interval-based and cron-based scheduling with configurable overlap policies (Allow, Skip, Buffer).
  • Workflow cancellation. Cancel running workflows and trigger registered cleanup handlers.
  • State pruning. Automatically remove old step outputs to keep large workflow state manageable.
  • Worker concurrency. --workers=N forks multiple processes with automatic restart on crash.
  • Full observability. Database logging, per-handler metrics, and event webhooks.
  • Testing utilities. QueueFake records dispatched jobs and batches for assertions in unit tests.
  • PHP attributes. #[QueuetyHandler('name')] for zero-config handler registration.

How it works

Workers boot from a minimal bootstrap that parses wp-config.php via regex (not require) to extract database credentials, then connects directly via PDO. No plugins, themes, or hooks are loaded. For handlers that need WordPress (like wp_mail), Queuety can optionally boot it per-handler.

Workflows break long-running work into steps. Each step persists its output to a shared state bag in the database. If PHP dies mid-step, the worker retries that step with all prior state intact. The step boundary is a single MySQL transaction: state update, job completion, and next step enqueue all happen atomically.

Workflow: generate_report (3 steps)

Step 0: fetch_data     -> state: {user_data: {...}}
Step 1: call_llm       -> PHP dies -> retry -> state: {user_data: {...}, llm_response: "..."}
Step 2: format_output  -> state: {user_data: {...}, llm_response: "...", report_url: "/reports/42.pdf"}

Workflow: completed

Requirements

  • PHP 8.2 or later
  • WordPress 6.4 or later
  • MySQL 5.7+ or MariaDB 10.3+

On this page