The ready count climbs, and the first proposal is more workers. Sometimes that is the right purchase. Often it copies the same wait onto more processes. A backlog is a number of jobs that have not finished. It is not, by itself, a shortage of consumers. Before you add workers, separate jobs that are waiting to run from jobs that are failing, jobs that are stuck on a lock, and jobs that were never picked up because the consumers you already pay for are not running.

This note is for the person who can open the queue and the worker host. It is not a cache expiry herd, not a process that grows until the machine kills it, not a case for putting Node beside Laravel, and not a reading of the slow query log. Those are real problems with their own checks. If you skip the split below, a larger consumer pool will make the wrong problem louder.

1. Split the backlog by state before you split the bill

Count four piles, not one chart. Ready: jobs a free worker could take now. Delayed: jobs that must wait until a time you chose. Reserved: jobs a worker already took and has not finished or released. Failed: jobs that errored and are no longer in the ready pile, or that will return there on a retry schedule. A single "depth" hides which pile is growing. More workers drain ready work. They do not repair a failed pile, and they do not make a delayed job due sooner.

Write the counts down with a clock time. Look again after a quiet ten minutes, without a deploy in between. If ready stays high and reserved stays near zero, workers are not taking work. If reserved stays high and ready stays low, workers are busy or stuck, and adding more may help only when those reserved jobs are healthy work rather than a lock. If failed grows while ready looks fine, you do not have a capacity problem. You have a job that errors.

Name the queue. A default queue mixed with mail, exports, and webhooks will show one depth for three kinds of work. If you can, read depth per queue or per job name. The fix for a stuck export is not the fix for a webhook that the partner keeps rejecting.

2. Find the job name that owns the depth

Open a sample of the ready pile and group it by type. One export, one image rebuild, or one webhook retry often accounts for almost all of the rows. The rest of the product is fine. A proposal to double the whole consumer fleet is a proposal to scale a single job you have not read.

Read one payload of that type. You want the identifier it carries, the tenant or account if there is one, and the outside call it makes. A backlog of identical retries against one broken endpoint is not load. It is one failure repeated. Park or fix that type before you add consumers. Otherwise every new worker joins the retry.

Check the age of the oldest ready job, not only the count. A thousand jobs from the last minute is a burst. A hundred jobs whose oldest is six hours is a drain that cannot keep up, or a consumer that stopped. Those are different afternoons. The burst may need a slower producer. The six-hour tail needs a consumer that is actually alive, or a job that finishes.

3. Watch one reserved job, not the average CPU

Pick one job that has been reserved longer than its usual run. See what it is waiting on. A lock held by another worker. An HTTP call with no timeout. A query that should have been read in the slow log before anyone talked about workers. A file on a disk that is full. More workers multiply a lock wait. They also multiply calls to a partner that already rate-limits you.

Timeouts belong in this look. A job with no timeout can sit in reserved until something else kills the process. The queue still counts it as taken. New workers start, and the stuck one keeps the slot. Set a bound that matches the work, and decide what a timeout means: retry, or fail with a reason a person can read. A retry without a bound is how a backlog refills itself.

If the wait is a query, stop. Adding consumers will run that query more often. The neighboring check is how to read the slow log before you refactor. If the wait is a memory climb inside one process, that is a different shape from a backlog. Do not buy workers as a substitute for either reading.

4. A retry can counterfeit a capacity problem

A payload that fails every time and returns to ready will look like depth. The worker is busy. The count does not fall. The error is the same line, over and over. That job is poison. More workers process the poison faster and still never finish it. Cap the attempts. After the cap, leave it in a failed store a person can open, with the last error and the identifier. Do not send it back to ready until someone changes the payload or the handler.

Watch the attempt count on the jobs that do succeed. A healthy job finishes on the first try. A job that often needs three tries is already telling you the handler or the partner is unstable. Scaling workers before you read that pattern spends money on retries. It also raises the chance of a double side effect if the handler is not safe to run twice. A second run that charges, emails, or inserts again is worse than a backlog. The runtime does not give you that safety. The handler has to record the result and stop when the same work arrives again.

Failed jobs that nobody looks at become a second backlog. If the team only watches the ready count, the failed store can grow for a week. Put one glance at failed next to the glance at ready. If failed is where the pain is, the next hire is not a worker. It is a fix, or a decision to drop a job type you cannot complete.

5. Count the workers you already have

Ask how many consumer processes should be running, and then count how many are running. A process manager can say four and have zero after a deploy, a crash, or a host that never started them. The ready pile grows because nothing is connected. Adding a fifth in a document does not start the first four. Check the host you think is consuming. Check that it is the environment that owns this queue, not a forgotten test box pointed at the same broker.

A database-backed queue has a second trap. Workers poll. If they are alive but sleeping on a long schedule, ready depth lingers even though "the workers are up." Shorten the sleep only after you know the jobs are cheap to peek at. Polling harder on a pile of poison still does not finish the poison.

Separate this from a cache stampede and from a second runtime. A key that expires and gets rebuilt by every request is a stampede, not a job backlog. A Node process beside the web app is a boundary decision, covered in when a queue should sit outside Laravel. Neither decision starts with "add workers" to a pile you have not named.

6. When more workers are the honest move

Add consumers when the checks above are boring. Ready stays high. Reserved jobs finish in their normal time. Failed is quiet. The oldest ready job keeps getting older while the workers you have are actually busy on useful work. The handler is safe to run twice. The partner can take the extra calls, or you have already limited the rate. Then a larger pool shortens the wait. Write down the ready count, the age of the oldest job, and the worker count before and after, so the next person sees that the add was earned.

If the producer can outrun any pool you are willing to run, more workers are a delay. Slow the producer, batch the work, or shed a job type. A backlog that grows without a bound is a product decision wearing an infrastructure costume.

When to ask for help

You can split ready, delayed, reserved, and failed in one sitting, and you can read one poison payload. Ask for help when the depth is real, the workers are alive, and you still cannot tell which job name owns the wait. Ask when a retry can charge or notify twice and nobody has proved the handler stops. Ask when two hosts might be consuming the same queue and nobody is sure which one is live. The work is to name the pile before you pay for a bigger pool.

Services cover that reading on an existing Laravel or Node system: the job that fills the queue, the reason it does not finish, and whether a worker is the fix. Selected work keeps each measurement next to the operation that produced it. Before you add another worker, the queue that keeps growing is performance and scalability work: the job that fills it, the reason it stalls, and how many consumers it really needs. If you write, send the queue name, the four counts, the job type that dominates, and whether the consumers you expect are actually running. Leave customer payloads out of the first message.