Привет, Хабр. С вами снова Вадим.
В прошлом посте я рассказывал, как написал 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к юзеров - это оверхед).
RichardMerlock
Прочиталось как "не один живой рекрутёр не ответил"... деформация?