Обновить
8K+
7
Евгений@grokfrog

Живу… хочу в Лос-Анджелес

10
Рейтинг
Отправить сообщение

Спасибо всем за звезды на гите, сейчас расплачусь...

Спасибо за замечание.

Я не из ЛА, я из Москвы. Текст, как и в прошлый раз, в основном нейросеть писала, я только идею и фактуру давал. К сожалению, я не редактор и не поэт, у меня в школе по русскому твёрдая троечка была

«Симметрические» поправлю на нормальное «симметричная». Спасибо, что ткнул. Буду стараться внимательнее проверять.

Прогнал локальный stress smoke, чтобы не отвечать «на глаз».

Сценарий был довольно жёсткий: большое количество отправок в реальном браузере, не напрямую через API. То есть путь был настоящий:

UI → frontend state/update → штатный send path приложения → backend → API response

Короткий автоматический прогон

На коротком автоматическом прогоне в 130 сообщений картина хорошая:

То есть на обычном сценарии сообщение появляется у отправителя практически мгновенно: p95 до optimistic render — меньше 100 ms.

Ручной stress-тест

Потом отдельно погонял ручной stress-тест: 2009 отправок в реальном браузере.

На этом уровне backend не развалился: все пойманные отправки получили HTTP 200.

Общие метрики ручного stress-теста

Деградация по мере роста количества сообщений

На длинной активной сессии деградация уже видна:

Самое интересное: сильнее всего растёт не только backend response time, а задержка до самого API request. Это указывает, что основной bottleneck появляется на клиенте: длинный список сообщений, React render/DOM, обработчики состояния, возможно IndexedDB/local state и отсутствие полноценной virtualization для длинной истории.

Короткий вывод

Интерфейс на обычном сценарии отзывчивый: десятки миллисекунд до optimistic render.

Backend на stress smoke тоже не развалился: 2009 отправок, 2009 успешных HTTP 200, без network failures.

Но при агрессивном long-chat stress на тысячах сообщений уже видно, где следующий инженерный шаг:

• virtualization списка сообщений;
• изоляция input-компонента от rerender списка;
• batching локальных updates;
• оптимизация обработки статусов/echo/realtime-событий;
• отдельный профайлинг decrypt/render длинной истории.

Важно: это локальный smoke/stress, а не production SLA. Ручной recorder в этом прогоне надёжно мерил send path до API 200; DOM render для длинной ручной сессии я буду мерить отдельным тестом открытия/скролла истории на 1000/5000/10000 сообщений.

Доброй ночи! ответ, на ваш комментарий ниже, мучался с разметкой, не туда написал)

Возможно, местами получилось слишком гладко. Учту, спасибо.

Чем ближе закат империи, тем безумнее её законы.

Дополнительно проверили WebSocket‑часть: на локальном стенде соединения держатся стабильно, а предел дальше упирается уже в ресурсы машины.

Привет! Спасибо за подробный разбор. Такой комментарий действительно полезен: по обычному демо в браузере часть подобных проблем легко не заметить, а при чтении кода они становятся видны сразу.

По нескольким пунктам я с вами согласен.

Замечание про N+1 в уведомлениях при обновлении профиля справедливое. В старом варианте логика действительно могла идти через цепочку “найти чаты пользователя → для каждого чата найти участников → для каждого участника найти пользователя → отправить событие”. Для небольшого демо это незаметно, но как паттерн для роста нагрузки такой подход неудачный.

То же самое с markChatAsRead(). В исходном варианте метод делал слишком много за один вызов: получал список сообщений, проходил по ним в цикле, обновлял receipt/status и отправлял отдельные события. На длинной истории это действительно плохой путь, особенно если чат активно используется.

После вашего комментария я отдельно прошёлся по backend и переработал эти участки.

Что было изменено:

— уведомления при обновлении профиля и обновлении списка чатов переведены на batch-запросы и отправку по уникальным получателям;
mark read / mark delivered переписаны через bulk SQL-операции вместо поштучного прохода по сообщениям;
— вместо множества отдельных status events добавлен bulk status event;
— timeline reactions теперь грузятся пачкой, без N+1 на каждое сообщение;
— список чатов получил пагинацию и сортировку ближе к БД;
— часть логики вынесена из контроллеров в сервисы;
Map<String, Object> в ключевых REST-ответах заменён на typed DTO;
— добавлены индексы под горячие запросы через миграцию V23__performance_indexes.sql;
— после рефакторинга обновлены тесты.

 Что было → стало
Что было → стало

Отдельно прогнал тесты и нагрузочные сценарии по direct-chat path. Backend сейчас проходит 192 теста. По k6 direct-chat battery получилось:

Суммарно по direct-chat battery:
Суммарно по direct-chat battery:

Самым важным результатом для меня было не просто отсутствие ошибок, а то, что mark read / mark delivered не начали деградировать на длинном прогоне. В 30-минутном soak:

При этом я не хочу выдавать эти результаты за полную production-готовность. Это пока проверка именно direct-chat HTTP/API-сценария. Групповые чаты, WebSocket load, сценарии с заранее набитыми 10k+ сообщений, поведение БД на больших объёмах и инфраструктурные лимиты ещё нужно проверять отдельно.

Поэтому я бы сейчас аккуратно сформулировал статус проекта так: это не production-ready мессенджер, но уже рабочий MVP/pet project, который постепенно приводится к более зрелому backend-состоянию.

Ваш комментарий помог подсветить места, которые на обычном демо не бросались в глаза. Спасибо за это. Если будет желание — буду рад продолжить предметно по query patterns, pagination strategy, receipt aggregation и group chat fanout.

█████████████████████████████████▶ 75% начинаю удаление репозитория…

Информация

В рейтинге
841-й
Откуда
Россия
Дата рождения
Зарегистрирован
Активность

Специализация

Бэкенд разработчик
Средний
Java