Привет, Хабр. С вами снова Вадим.
В прошлом посте я рассказывал, как написал AI-бота Аврора, который ищет вакансии вместо меня.
Статья залетела, и к нам пришли первые 100 тестеров. И тут началось веселье.
Представьте: вы нажимаете кнопку "Найти работу", а бот молчит. 10 секунд, 20 секунд. Вы думаете: "Сломалось" и жмете кнопку еще 5 раз.
А на самом деле бот не сломался. Он просто "ушел на кухню готовить".
В этой статье расскажу, как мы переписали архитектуру с "однорукого повара" на "промышленный конвейер", зачем использовали SQL вместо модного Redis и как наша скорость стала нашей проблемой.
Если вы разработчик - найдете тут код про SKIP LOCKED.
Если вы ищете работу - поймете, почему наш бот теперь быстрее, чем пальцы любого рекрутера.
Проблема: Эффект "Одного Официанта"
Первая версия бота работала по простому принципу (синхронно).
Юзер пишет: "Хочу вакансии".
Бот (скрипт) бежит на хедхантер, качает данные, бежит в нейросеть (Gemini), пишет письмо и возвращается к юзеру.
Это занимает ~15 секунд.
Проблема: Пока бот бегал для одного пользователя, остальные 99 стояли в очереди и видели "зависший" экран.
Telegram не любит, когда боты тупят, и просто обрывал соединение.
Нам нужно было разделить бота на две части:
Официант (Интерфейс): Только принимает заказы. "Принял, ожидайте". Это занимает 0.1 секунды.
Кухня (Воркеры): Армия поваров, которые где-то в подсобке жарят ваши резюме и варят сопроводительные письма.
Техническое решение: Очередь на PostgreSQL
Обычно для таких "кухонь" используют сложные инструменты типа Celery или RabbitMQ.
Но мы стартап, у нас нет лишних денег на сервера.
Поэтому мы сделали очередь прямо в базе данных (PostgreSQL).
Для технарей: мы использовали магию SELECT ... FOR UPDATE SKIP LOCKED.
Для не технарей: представьте, что у нас есть доска с заказами.
Подходит повар (Воркер), берет первый листок.
В обычном мире два повара могут схватиться за один листок и порвать его.
Команда SKIP LOCKED работает как магия: повар видит, что листок уже в чужой руке, и мгновенно берет следующий.
Никаких драк, никаких простоев.
Теперь мы можем запустить хоть 50 "поваров" одновременно. Бот стал отвечать мгновенно.
Факап: Мы стали слишком быстрыми
Мы выкатили обновление. Интерфейс "залетал". Очереди исчезли.
Мы радовались ровно 10 минут.
Потом легли наши логи.
Ошибка: 429 Too Many Requests от hh.ru.
Оказалось, что наша армия цифровых поваров начала работать так быстро, что сайт с вакансиями принял нас за хакерскую атаку (DDOS).
Раньше "тормоза" бота были естественным ограничителем. Теперь, когда тормозов не стало, мы вычерпали лимиты запросо�� за минуту.
Пришлось учить роботов быть людьми.
Мы внедрили искусственное замедление (Rate Limiter).
Теперь бот делает паузы. Он "дышит". Он имитирует поведение человека, который пьет кофе между откликами.
Это парадокс разработки: сначала ты тратишь ночи, чтобы ускорить код, а потом пишешь костыли, чтобы его замедлить.
Что это дает пользователю?
Надежность. Ваш запрос на поиск работы не потеряется, даже если сервер перезагрузится. Он записан в "блокнот" на кухне.
Скорость. Вы можете накидать боту 50 задач (например, "откликнись во все банки") и закрыть Телеграм. Он будет шуршать в фоне, вежливо соблюдая паузы, чтобы вас не забанили.
Результат. За вчерашний день наши "вежливые воркеры" обработали 1500 сложных заявок с AI-генерацией писем. Ни один живой рекрутер с такой скоростью не работает.
Мы не используем модные технологии ради моды. Мы используем SQL, потому что это надежно как автомат Калашникова.
Сейчас мы учим бота не просто искать вакансии, а переписывать ваше резюме так, чтобы HR плакали от счастья. Но об этом (и о том, как заставить нейросеть выдавать JSON) - в следующей серии.
Кому интересно следить за тем, как мы строим "Uber для поиска работы" - велкам в канал. Там мы выкладываем и код, и кейсы трудоустройства.
(В комментах готов пояснить за архитектуру и почему RabbitMQ для бота на 10к юзеров - это оверхед).