Queue depth without throughput says nothing — a hundred jobs is fine at fifty a second and an incident at one a minute.
// the metric: the age of the oldest waiting job
foreach (['interactive', 'bulk'] as $queue) {
$gauge->set($this->oldestPendingAgeSeconds($queue), ['queue' => $queue]);
}
// and the scaling rule, per queue
// interactive: scale up above 30s, down below 5s
// bulk: scale up above 30m, down below 5m
Oldest-waiting-age measures the promise directly and needs no interpretation, where depth needs dividing by a throughput nobody has to hand. Different thresholds per queue is the reason for having split them, and a heartbeat job enqueued on a timer gives the same signal without instrumenting broker internals.