Job queue – dealing with stuck records
When looking at the code from the claimItem method it only returns FALSE if no items are available to claim.

So if $queue-&gt;claimItem() returns FALSE you can be sure that all available items are claimed (or failed to be claimed).

public function claimItem($lease_time = 30) {
  // Claim an item by updating its expire fields. If claim is not successful
  // another thread may have claimed the item in the meantime. Therefore loop
  // until an item is successfully claimed or we are reasonably sure there
  // are no unclaimed items left.
  while (TRUE) {
    try {
      $item = $this-&gt;connection-&gt;queryRange('SELECT data, created, item_id FROM {' . static::TABLE_NAME . '} q WHERE expire = 0 AND name = :name ORDER BY created, item_id ASC', 0, 1, [':name' =&gt; $this-&gt;name])-&gt;fetchObject();
    }
    catch (\Exception $e) {
      $this-&gt;catchException($e);
      // If the table does not exist there are no items currently available to
      // claim.
      return FALSE;
    }
    if ($item) {
      // Try to update the item. Only one thread can succeed in UPDATEing the
      // same row. We cannot rely on REQUEST_TIME because items might be
      // claimed by a single consumer which runs longer than 1 second. If we
      // continue to use REQUEST_TIME instead of the current time(), we steal
      // time from the lease, and will tend to reset items before the lease
      // should really expire.
      $update = $this-&gt;connection-&gt;update(static::TABLE_NAME)
        -&gt;fields([
          'expire' =&gt; time() + $lease_time,
        ])
        -&gt;condition('item_id', $item-&gt;item_id)
        -&gt;condition('expire', 0);
      // If there are affected rows, this update succeeded.
      if ($update-&gt;execute()) {
        $item-&gt;data = unserialize($item-&gt;data);
        return $item;
      }
    }
    else {
      // No items currently available to claim.
      return FALSE;
    }
  }
}