Безопасность и правоРазбор · На радарHabr AI→ оригинал

Вайбкод-бот для видео не выдержал второго пользователя: как его довели до шести VPS

Друг собрал с ИИ Telegram-бота UNIQ, который делает из одного ролика несколько копий для Reels, Shorts и TikTok. Прототип работал, пока пользователь был один. Со вторым вылезли блокировки, переполненный диск и задания, которые теряются при падении воркера. Авторы перешли на PostgreSQL, очередь с арендой и S3, и теперь сервис живёт на шести VPS.

AI-обработка оригинала Habr AI; редакция Hamidun News
Вайбкод-бот для видео не выдержал второго пользователя: как его довели до шести VPS
Источник: Habr AI. Коллаж: Hamidun News.
Слушать статью
Паспорт материала и цифры
ПараметрДанные
ПервоисточникHabr AI ↗
Главный редактор
Ключевой факт #1Аренда задания с токеном владельца не даёт проснувшемуся воркеру портить заказ
Больше воркеров не значит быстреена 5 CPU пик пришёлся на 4–5 процессов
Ключевой факт #3Блокировка на пользователя вместо общей ускорила волну с 5,9 до 3,2 с

Вайбкодинг отлично работает, пока у проекта один пользователь, и этот пользователь вы сами. Разработчик UNIQ рассказал на Habr, как Telegram-бот, собранный другом с помощью ИИ, превращался в сервис, который не стыдно продавать. Главный вывод: код написать быстро, а вот определить, что считать правильным поведением системы, нужно самим.

Что было и где сломалось

UNIQ принимает ролик и выдаёт от одной до десяти версий для повторной публикации: FFmpeg меняет параметры кадра, темпа, цвета и звука. Прототип работал на Python с SQLite и жил на Mac автора. Первой проблемой стал диск: у заказа одновременно лежат исходник, временные файлы и готовые копии, поэтому добавили лимит загрузки в 150 МБ, дисковый бюджет и очистку.

Настоящий перелом случился, когда в тесте появился второй пользователь. Пока FFmpeg обрабатывал чужой ролик, он открывал меню и слал своё видео, и всё упёрлось в общие блокировки и разбросанное состояние. Переписывать на Go не стали: смена языка не убирает общую блокировку и не добавляет ядер. Вместо этого разделили сетевое ожидание, запросы к базе и кодирование.

Очередь, которая переживает падения

Для раздачи заданий по нескольким серверам взяли PostgreSQL: общая база, транзакции и блокировки строк через FOR UPDATE SKIP LOCKED. Задание выдаётся воркеру в аренду. Если воркер завис, по истечении срока заказ берёт другой. Чтобы очнувшийся первый воркер не затёр результат, при выдаче создаётся новый токен владельца, и каждая запись состояния его проверяет.

План эффектов сохраняют заранее, а готовые копии записывают в checkpoint, так что второй воркер продолжает с места падения. Повторный запрос с тем же идентификатором возвращает сохранённый receipt, а не выполняет операцию заново. Честная граница тоже названа: если файл ушёл в Telegram, а запись об отправке не успела сохраниться, копия может прийти дважды. Идемпотентность внутри системы не делает внешний API exactly-once.

Отдельная история с монетами. Это оказалась небольшая бухгалтерия: партии со своими сроками, резервы и история операций. При отмене заказа неиспользованные монеты возвращаются в исходные партии с прежним сроком, иначе отмена превращается в бесконечный абонемент. Заказ фиксирует тариф на момент подтверждения, а повторное платёжное уведомление не создаёт второе начисление благодаря уникальному ID внешней транзакции.

Заметный пользователю выигрыш дала не оптимизация, а перестановка: кодирование отделили от выдачи, и копии отправляются по одной. В прогоне на локальном стенде они пришли через 40, 70, 105, 136 и 171 секунду, то есть первая появилась на 131 секунду раньше конца заказа.

Цифры нагрузки и их границы

После выноса видео в S3 и подключения PgBouncer (до 2000 клиентских соединений на 10 серверных) начали измерять узкие места. Волна из 1000 подтверждений заказов шла 5,9 секунды, при этом суммарное ожидание одной SQL-блокировки доходило примерно до сорока секунд. Общий исключительный lock заменили блокировками по пользователям, и волна сократилась до 3,2 секунды.

Увеличение пула приложения с 8 до 16 соединений при пределе PgBouncer в 10 ничего не ускорило, очередь просто переехала. Кодирование 20 коротких роликов 720×1280 на 5 CPU вышло на плато уже на 4–5 воркерах: около 29 заданий в минуту. Двенадцать процессов съели 3133 МиБ памяти и работали медленнее пяти.

Меню на 10 000 первых обращений обработалось без ошибок, но p95 составил около 30 секунд. Авторы справедливо замечают: принять запрос и быстро ответить человеку не одно и то же.

Что из этого забрать себе

Цифры сняты на коротких роликах и конкретном стенде, переносить их на длинное 1080p или на число пользователей нельзя. Зато набор вопросов универсален. Кто кого блокирует? Где живёт состояние? Что будет, если воркер умрёт посреди заказа? Что произойдёт при повторе платёжного события? ИИ быстро пишет код, но ответы на эти вопросы придётся сформулировать и проверить тестами самому, лучше до прихода второго пользователя.

Сюжет

Вопросы и ответы по материалу

Что известно по теме: что было и где сломалось?

UNIQ принимает ролик и выдаёт от одной до десяти версий для повторной публикации: FFmpeg меняет параметры кадра, темпа, цвета и звука. Прототип работал на Python с SQLite и жил на Mac автора. Первой проблемой стал диск: у заказа одновременно лежат исходник, временные файлы и готовые копии, поэтому добавили лимит загрузки в 150 МБ, дисковый бюджет и очистку. Настоящий перелом случился, когда в тесте появился второй пользователь.

Что известно по теме: очередь, которая переживает падения?

Для раздачи заданий по нескольким серверам взяли PostgreSQL: общая база, транзакции и блокировки строк через FOR UPDATE SKIP LOCKED. Задание выдаётся воркеру в аренду. Если воркер завис, по истечении срока заказ берёт другой. Чтобы очнувшийся первый воркер не затёр результат, при выдаче создаётся новый токен владельца, и каждая запись состояния его проверяет. План эффектов сохраняют заранее, а готовые копии записывают в checkpoint, так что второй воркер продолжает с места падения.

Что известно по теме: цифры нагрузки и их границы?

После выноса видео в S3 и подключения PgBouncer (до 2000 клиентских соединений на 10 серверных) начали измерять узкие места. Волна из 1000 подтверждений заказов шла 5,9 секунды, при этом суммарное ожидание одной SQL-блокировки доходило примерно до сорока секунд. Общий исключительный lock заменили блокировками по пользователям, и волна сократилась до 3,2 секунды. Увеличение пула приложения с 8 до 16 соединений при пределе PgBouncer в 10 ничего не ускорило, очередь просто переехала.

ЖХ
Жемал Хамидун — CPO Alpina Digital, продакт-менеджер AlpinaGPT. Делает AI-продукты с 2019 года. Hamidun News — pet project, который вырос в production-агрегатор с 6000+ читателей.
✦ Единое AI-пространство · Nomad

Протестируйте новые LLM и мультимодальные модели в одном окне

Платформа Nomad AI (nomadnet.ai) даёт прямой доступ к Claude Opus 5.5, GPT-6, Gemini 3.1, DeepSeek и автономным агентам без VPN, зарубежных карт и зоопарка подписок.

Открыть Nomad AI →Автономный Nomad Agent
Что вы думаете?
Загружаем комментарии…