← Все статьи

Очередь фоновых задач на PostgreSQL и Go: проект Swig

Таблица jobs, блокировки строк, JSONB, LISTEN/NOTIFY и воркеры — без Redis, если Postgres уже в стеке.

Содержание

Коротко

Не каждая медленная операция должна блокировать HTTP-ответ: письма, webhooks, ресайз изображений, отчёты — классические фоновые задачи. На Dev.to разобрали Swig — очередь на Go с хранением jobs в PostgreSQL, без отдельного брокера сообщений.

Что произошло

Автор исходит из простой мысли: если Postgres уже крутится в проде, зачем поднимать Redis «только ради очереди»? Swig показывает, как уложить фоновую работу в ту же СУБД, опираясь на транзакции и блокировки строк.

Архитектура выглядит так. Есть таблица jobs с payload в JSONB, статусами, счётчиком попыток и метками времени. Воркеры на Go забирают задачу внутри транзакции — два процесса не должны взять одну и ту же строку. Рядом реестр воркеров и ведущий процесс для обслуживания: протухшие lease, повторные попытки, уборка старых записей.

Интерфейсы в Go описывают поведение обработчика; goroutines дают параллелизм; context — отмену и таймауты на выполнение. Чтобы не крутить бесконечный опрос БД, используют LISTEN/NOTIFY: после вставки job воркеры получают сигнал и просыпаются.

Пример домена в статье — надёжная обработка фоновых операций в веб-приложении, но паттерн переносится на любой сервис, где важны повторы и идемпотентность.

Почему это важно

Многие команды по умолчанию добавляют RabbitMQ или Redis, хотя нагрузка — десятки–сотни задач в секунду, а критичнее предсказуемость и один контур эксплуатации. Очередь на Postgres делит бэкапы, мониторинг и миграции с основным приложением.

Честное ограничение: при очень высоком QPS и жёстком SLA на задержку доставки специализированный брокер может выиграть. Для типичного веб-продукта схема «таблица + воркеры» живёт годами — особенно если команда уже умеет админить Postgres лучше, чем второй data store.

На практике

  1. Спроектируйте схему job: id, kind, payload jsonb, status, attempts, run_at, locked_at, locked_by.
  2. Забирайте задачу в транзакции с FOR UPDATE SKIP LOCKED (или эквивалентом в вашей версии).
  3. Делайте обработчики идемпотентными — повтор после сбоя не должен дважды отправить письмо или списать деньги.
  4. Шлите NOTIFY после COMMIT вставки; раз в N секунд оставьте резервный опрос на случай пропущенного сигнала.
  5. Выделите ведущий процесс для requeue зависших jobs и архивации истории.
  6. Метрики: глубина очереди, возраст старейшей pending-задачи, доля failed.

Итог

Swig на Dev.to — хороший учебный каркас очереди на PostgreSQL в Go: блокировки, JSONB, NOTIFY, контексты. Если Postgres уже в стеке, имеет смысл оценить такой контур до покупки второго хранилища. Код и детали схемы — в оригинале.