Есть редкие моменты, когда в Python-мире появляется инструмент, который не просто «ещё один аналог», а реально меняет привычный способ думать об архитектуре. Выход oban-py — как раз такой случай. Это не Celery с другим API и не очередной thin-wrapper вокруг Redis. Это попытка честно перенести философию Elixir-овского Oban в Python — со всеми её сильными и спорными сторонами.
И если коротко: Oban — это про доверие базе данных. Максимальное.
Основная часть
Почему вообще Oban — это не «очередь задач»
В классическом Python-стеке мы привыкли к стандартному набору:
🧱 приложение
📬 брокер (Redis / RabbitMQ)
⚙️ воркеры
🧾 база данных
Oban выбрасывает середину. Очередь задач живет прямо в PostgreSQL. Не рядом. Не «через». А буквально — таблица oban_jobs, транзакции, блокировки, LISTEN / NOTIFY.
Это сразу звучит либо гениально, либо безумно — и именно поэтому идея цепляет.
Главная фишка: транзакционная атомарность
Самый сильный аргумент в пользу Oban — атомарность бизнес-логики.
🧠 Создаёшь пользователя
🧠 В той же транзакции ставишь задачу на отправку email
🧠 Если что-то падает — не будет ни пользователя, ни письма
В Celery это достигается костылями, паттернами и дисциплиной. В Oban — по умолчанию. Потому что задача — это просто ещё одна строка в БД.
И вот здесь PostgreSQL внезапно превращается из «хранилища данных» в координатор распределённой системы.
Как Oban-py реально работает под капотом
Вставка задачи — и магия NOTIFY
Ты пишешь простой, чистый код:
@job(queue="default")
async def send_email(to: str, subject: str, body: str):
await smtp.send(to, subject, body)
await send_email.enqueue("user@example.com", "Hello", "World")
Под капотом происходит больше, чем кажется:
⚙️ задача попадает в oban_jobs со статусом available
⚙️ PostgreSQL делает NOTIFY по каналу insert
⚙️ все Oban-ноды, которые слушают этот канал, мгновенно просыпаются
Никаких polling-циклов, никаких «проверяем каждые N секунд». Чистая реактивность на уровне базы.
FOR UPDATE SKIP LOCKED — сердце всей системы
Самый важный SQL во всей архитектуре:
SELECT ...
FROM oban_jobs
WHERE state = 'available'
ORDER BY priority, scheduled_at
FOR UPDATE SKIP LOCKED
Почему это так важно:
🧩 FOR UPDATE — никто другой не может взять эту задачу
🧩 SKIP LOCKED — если кто-то уже взял, не ждём, а идём дальше
Результат:
🚀 несколько продюсеров забирают задачи параллельно
🚀 никакого «залипания» на одной строке
🚀 никакого двойного исполнения
Это ровно тот момент, где PostgreSQL оказывается лучшим брокером, чем многие специализированные системы.
Асинхронность без параллелизма — честно и прямо
OSS-версия Oban-py работает так:
⚠️ один event loop
⚠️ asyncio.create_task
⚠️ отличная конкуррентность для I/O
⚠️ нулевая терпимость к CPU-bound задачам
И здесь разработчики предельно честны: если вам нужен реальный параллелизм — берите Pro. Там автоматически подключается пул процессов, без переписывания кода.
Мне это нравится. Нет маркетинга в стиле «async решит всё». Есть ясное ограничение и понятный апгрейд-путь.
Pattern matching по-питоновски — неожиданно элегантно
Результат выполнения job-а обрабатывается через match:
🧠 exception → retry или discard
🧠 Snooze → отложить
🧠 Cancel → отменить
🧠 ok → completed
Это почти прямой привет Elixir-у, и реализовано удивительно чисто. Код читается не как «фреймворк», а как нормальная программа. Редкость.
Лидер, спасатель и уборщик — всё в базе
Oban не боится сложных распределённых задач, но решает их максимально просто.
Лидер-элекшн
🗳️ одна таблица
🗳️ lease с TTL
🗳️ INSERT ... ON CONFLICT
Никакого Raft, ZooKeeper и прочей тяжёлой артиллерии. Если лидер умер — TTL истёк, следующий стал лидером. Всё.
Спасение зависших задач
🛟 если job «исполняется» дольше rescue_after
🛟 его возвращают в очередь
🛟 счётчик rescued растёт
Да, возможны ложные срабатывания. Поэтому:
⚠️ делайте задачи идемпотентными
⚠️ увеличивайте rescue_after
Это честный трейд-офф в OSS-версии.
Pruner — чтобы таблица не росла вечно
🧹 удаляет завершённые задачи старше max_age
🧹 делает это батчами
🧹 не блокирует прод
Backoff без «стада бизонов»
Retry-механика — одна из самых аккуратных, что я видел:
📉 экспоненциальный рост
📉 jitter (случайное смещение)
📉 clamping для больших max_attempts
Результат:
❌ нет шипов нагрузки
❌ нет синхронных повторных падений
✅ система дышит ровно
Мелочь? Нет. Именно такие детали отличают «очередь» от продакшен-инструмента.
Моё личное мнение
Oban-py — это библиотека, сделанная инженерами для инженеров.
🧠 минимум магии
🧠 максимум использования того, что уже есть
🧠 PostgreSQL — не костыль, а платформа
Да, это не серебряная пуля.
⚠️ CPU-heavy задачи — мимо OSS
⚠️ большие объёмы — почти гарантированно Pro
⚠️ нужно понимать базу, а не «просто поставить»
Но если вы:
✅ уже используете PostgreSQL
✅ хотите меньше инфраструктуры
✅ цените транзакционную целостность
✅ устали от зоопарка сервисов
— Oban-py реально стоит внимания.
Заключение
Oban-py — это не просто порт фреймворка из Elixir. Это другой взгляд на фоновые задачи в Python.
Не «давайте добавим ещё один сервис», а
👉 «давайте использовать базу данных по-взрослому».
Мне кажется, именно такие проекты и двигают экосистему вперёд: не количеством звёзд, а качеством идей. И если Oban-подход приживётся в Python — мы ещё не раз увидим, как очереди задач становятся… просто частью схемы БД.
И, честно говоря, это очень красивая мысль.
Источники
- Оригинальная статья:
https://www.dimamik.com/posts/oban_py/ - PostgreSQL FOR UPDATE SKIP LOCKED:
https://www.postgresql.org/docs/current/sql-select.html#SQL-FOR-UPDATE-SHARE - PostgreSQL LISTEN / NOTIFY:
https://www.postgresql.org/docs/current/sql-notify.html - Oban (Elixir):
https://getoban.pro