Первая тысяча пользователей: что ломается первым
Не сервер и не нагрузка, а запросы в цикле, пропущенные индексы, тяжёлая работа внутри запроса и лимиты почты. Как найти это заранее.
Тысяча пользователей — это немного. Сервер, который тянул демо, обычно справится и с ними. Ломается не железо, а допущения, которые были верны, пока в базе было двадцать строк и один тестовый аккаунт. Хорошая новость: эти поломки довольно предсказуемы.
Запросы в цикле
Самая частая причина медленных страниц в коде, написанном с ИИ, — проблема N+1. Страница загружает список заказов одним запросом, а потом для каждого заказа отдельным запросом подтягивает клиента, отдельным — товары, отдельным — статус доставки. На десяти заказах это тридцать быстрых запросов, и никто ничего не замечает. На пятистах — полторы тысячи, и страница открывается несколько секунд.
Как найти: включи логирование запросов к базе в разработке и открой любую страницу со списком. Если число запросов растёт вместе с числом строк на экране, это оно. Лечится загрузкой связанных данных одним запросом — через join, in (...) или встроенную подгрузку связей в ORM.
Пропущенные индексы
Пока таблица маленькая, база читает её целиком за миллисекунды, и отсутствие индекса незаметно. Потом таблица растёт, и тот же запрос начинает занимать сотни миллисекунд — причём сразу у всех.
Первые кандидаты на индекс:
- внешние ключи — Postgres не создаёт для них индекс автоматически;
- колонки, по которым фильтруют чаще всего: владелец записи, статус, дата;
- колонки, по которым сортируют списки с пагинацией.
Проверить конкретный запрос помогает explain analyze: если в плане видно последовательное чтение большой таблицы ради нескольких строк, индекса не хватает. Только не добавляй индексы на всё подряд — каждый замедляет запись.
Списки без пагинации
Эндпоинт, который отдаёт «все проекты пользователя» или «все сообщения в чате», работает, пока их пять. У активного пользователя их становится несколько тысяч, и ответ весит мегабайты. Пагинацию проще добавить сейчас, чем потом объяснять, почему у самых преданных клиентов приложение тормозит сильнее всех.
Тяжёлая работа внутри запроса
Отправка письма, генерация PDF, обращение к ИИ, обработка картинки — всё это часто выполняется прямо в обработчике HTTP-запроса. Человек ждёт, соединение держится, и если внешний сервис отвечает медленно, запрос падает по таймауту. Хуже всего, когда действие при этом наполовину выполнилось: заказ создан, а письмо не ушло.
Такую работу выносят в очередь фоновых задач. Запрос сохраняет данные и ставит задачу, задача выполняется отдельно, с повторами при ошибке. Человек получает ответ сразу, а не после того, как сторонний сервис соизволит ответить.
Лимиты почты
Письма с подтверждением, сбросом пароля и уведомлениями внезапно становятся частью критического пути. И у почты есть лимиты, о которых узнают в момент, когда они кончились: суточный предел у почтового ящика или бесплатного тарифа, ограничение на скорость отправки, блокировка за жалобы на спам.
Что проверить:
- какой у отправителя суточный лимит и сколько писем ты отправляешь сейчас;
- настроены ли SPF, DKIM и DMARC — без них письма уходят в спам у заметной части получателей;
- не идут ли рассылки и письма входа через один и тот же канал. Если маркетинговая рассылка выберет лимит, люди перестанут получать коды для входа.
Соединения с базой
Серверные функции на платформах вроде Vercel или Netlify открывают соединение с базой на каждый экземпляр. При росте трафика экземпляров становится много, и база упирается в предел одновременных соединений. Снаружи это выглядит как случайные ошибки, которые появляются и пропадают. Решение — пулер соединений между функциями и базой; у управляемых баз он обычно уже есть, его просто нужно включить и использовать правильную строку подключения.
Как найти это заранее
Самый дешёвый способ — сгенерировать на стейджинге в десять раз больше данных, чем у тебя сейчас, и пройти основные сценарии. Медленные страницы видно сразу. Вторая привычка — журнал медленных запросов в продакшне: всё, что дольше, скажем, полусекунды, записывается, и раз в неделю кто-то на это смотрит.
SUBXCAT может пройтись по проекту до того, как эти места найдут пользователи, и починить самые дорогие из них.