Craft's queue slows to a crawl with big jobs — the index fix that shipped in 5.11
Queue performance bugs are usually boring to read about because the fix is "add an index" and the interesting part — why the existing indexes weren't already doing the job — gets skipped. Craft CMS issue #19097 is worth reading in full because the maintainer's first response was "I can't reproduce this," and the reason he couldn't is the actual lesson.
The symptom didn't match the usual suspect
The reporter was running Craft 5.10.5, PHP 8.5, MySQL 8.4.7, with Commerce order-email and catalog-pricing jobs whose serialized payloads ran over 200 KB. The queue's reserve query — order by priority and ID, grab the next job — was taking "a couple of seconds" per call, which adds up fast when it's the thing standing between a page request and a response. The obvious hypothesis is row count: a queue with a lot of history in it should get slower to query. Brandon Kelly, maintaining the project, tried exactly that — a local queue with 150,000 rows — and saw no slowdown at all, around 0.1 seconds for the same kind of query.
The actual variable was payload size, not row count. MySQL's query planner, when it can't satisfy an ORDER BY from an index alone, falls back to reading the matching rows in full and sorting them in memory or on disk — a filesort. The queue table's job column holds the entire serialized payload, and it isn't part of the ORDER BY at all. But a filesort over rows with a 200 KB blob column means MySQL is hauling that blob through the sort step regardless of whether it's used for anything, because the row has to be read to get at the columns that actually matter. Row count alone doesn't reproduce that; payload size does.
The fix, and the number that makes it concrete
PR #19443 adds an index built around what the reserve query actually filters and sorts on — the channel, fail, and timeUpdated columns it filters by, with priority and id for the ordering — so MySQL can walk an index instead of falling back to a filesort over full rows. The PR author's own benchmark, reproduced with 20,000 jobs of 8 KiB each on MySQL 8.4, is the number I'd remember from this: EXPLAIN ANALYZE went from a 541 ms filesort to a 0.008 ms ordered index lookup. Four orders of magnitude, from one index targeting the actual access pattern instead of one that happened to exist already. It merged 18 August and shipped in Craft 5.11.0 (1 September 2026) via migration m260817_012148_queue_reserve_index.
What I actually checked on my own install
Confirmed I'm on 5.11.0 or later everywhere I run Craft, so the migration's already applied. But the more useful habit this reinforces is the reporter's own workaround while waiting for the fix: move large payload data out of the job row entirely, into its own table, and reference it by ID from a small job. That's not Craft-specific — it's the same advice that applies to Drupal's Queue API, where a job item gets serialized into the queue table too, and a job holding a large array or a full entity rather than an entity ID has the identical failure mode waiting for it, just without a core index tuned for the symptom yet. Keep queue rows small and point at your data; don't make the queue table carry it.