PostgreSQL SKIP LOCKED: разобрать очередь параллельно
FOR UPDATE SKIP LOCKED позволяет нескольким обработчикам брать разные свободные строки очереди, не ожидая запись, которую уже захватил другой обработчик. Используйте его для короткой транзакции выдачи задачи, а не как общий способ читать согласованный набор данных: результат намеренно пропускает заблокированные строки.
Выдать одну задачу и пометить её занятой
Предполагается, что jobs содержит поля id, status, started_at и payload. Один UPDATE ... RETURNING атомарно выбирает доступную задачу и возвращает её вызывающему обработчику:
WITH next_job AS (
SELECT id
FROM jobs
WHERE status = 'ready'
ORDER BY id
LIMIT 1
FOR UPDATE SKIP LOCKED
)
UPDATE jobs AS job
SET status = 'running', started_at = now()
FROM next_job
WHERE job.id = next_job.id
RETURNING job.id, job.payload;Если строка занята другим воркером, запрос возьмёт следующую; когда свободных задач нет, он вернёт ноль строк. Индекс по статусу и порядку обработки может помочь большой очереди, но подбирайте его по фактическому плану.
Держите транзакцию короткой: не выполняйте сетевую работу до фиксации захвата задачи. Добавьте срок аренды или отдельный механизм восстановления задач, оставшихся в running после сбоя воркера. Для пустой очереди обработчик должен спокойно дождаться следующей попытки.
Официальная справка: SELECT — locking clause.