Очередь в PostgreSQL: забираем задачу через SKIP LOCKED

Несколько воркеров ищут первую задачу со статусом ready. Если каждый делает SELECT, а затем отдельный UPDATE, они могут выбрать один id. Даже когда обработчик умеет переживать повтор, одинаковая тяжёлая работа расходует ресурсы и задерживает остальные задачи.

Выбор и смена состояния должны происходить в одной короткой транзакции. FOR UPDATE блокирует выбранные строки, а SKIP LOCKED позволяет другому воркеру пройти дальше, не ожидая задачу, уже занятую соседом. Это подходит для распределения работы, где строгая полнота текущего снимка не требуется.

Как изменить решение

BEGIN;
WITH picked AS (
  SELECT id FROM jobs
  WHERE status = 'ready'
  ORDER BY priority DESC, id
  FOR UPDATE SKIP LOCKED
  LIMIT 1
)
UPDATE jobs AS j
SET status = 'running', claimed_at = clock_timestamp()
FROM picked AS p
WHERE j.id = p.id
RETURNING j.id, j.payload;
COMMIT;

Два соединения смогут забрать разные задачи. Если первая по приоритету заблокирована, второе возьмёт следующую доступную. Поэтому такая очередь не гарантирует, что задачи начнут выполняться строго по приоритету во всех воркерах.

Не держите транзакцию открытой на время внешнего HTTP-запроса или многоминутного расчёта. Сначала зафиксируйте получение, затем выполняйте работу. Для восстановления после падения понадобятся срок аренды, владелец и условное завершение, чтобы старый воркер не отметил готовой задачу, уже переданную новому.

Что проверить на своих данных

В двух соединениях выполните блок выбора без COMMIT первого. Сравните полученные id: они должны отличаться. Затем завершите первое соединение без фиксации и убедитесь, что его задача снова доступна. Отдельно проверьте пустую очередь: это штатный результат, после которого воркер делает паузу.

Практический индекс должен поддерживать поиск ready и порядок выбора, например частичный индекс по priority DESC, id с условием status = ready. Но индекс выбирают по плану и нагрузке: частая смена статуса тоже требует работы. Метрики очереди лучше считать отдельным запросом без SKIP LOCKED, иначе занятые строки будут исчезать из наблюдения.

Практика дальше

Очередь задач — один из способов распределения работы. Если дальше нужен событийный конвейер, Data Engineering Fast-Track даёт практику Kafka, Spark и Airflow: выбор ключей и партиций, batch и streaming, расписания и backfill. Итогом станет локальный pipeline от событий до lakehouse.

Посмотреть программу и практику курса

Источник: Очередь в PostgreSQL — официальная документация.

Другие задачи по теме: PostgreSQL: запись, блокировки и планы запросов.

Продолжить на практике

  • Data Engineering Fast‑Track: Kafka → Airflow → Spark ↗

    Практический fast-track по data engineering: за 4–6 недель собираем рабочий конвейер Kafka → Spark → Lakehouse под управлением Airflow. Разберём batch и streaming, окна и watermark, схемы и контракты. Мини‑проекты, Docker Compose, шаблоны DAG и пайплайнов. Курс подойдёт инженерам, аналитикам и тем, кто хочет освоить современные пайплайны данных с уверенным Python.