# Адриан из JavaScript Mastery: «Ваше приложение сломается именно в таком порядке»

Источник: https://www.youtube.com/watch?v=EaXHfuHRWwg
Канал: JavaScript Mastery
Опубликовано: 04.09.2026

---

Сегодня создание полноценного приложения — фронтенда, бэкенда и базы данных — стало доступнее благодаря инструментам ИИ, но настоящая проверка навыков инженера начинается в момент роста популярности продукта. Автор канала **JavaScript Mastery** Адриан утверждает, что построить приложение никогда не было самой сложной частью; настоящий вызов — это проектирование систем (System Design), когда один сервер перестает справляться с нагрузкой. В этом материале рассматривается путь эволюции архитектуры: от простейшего сервера до распределенных баз данных, где каждый новый слой добавляется только тогда, когда предыдущий намеренно доводится до критической точки.

## 🚀 Начальная точка: Один сервер для тысяч пользователей
[[JUMP:01:39]]

Большинство современных приложений начинают свой путь с простейшей схемы: один сервер, исполняющий программный код, и одна база данных для хранения информации. По словам Адриана, такой конфигурации достаточно не для десятков, а для тысяч активных пользователей [01:57]. Многие проекты никогда не перерастают этот этап, и в этом нет ничего плохого — это стабильная и понятная архитектура.

Однако проблемы начинаются при резких всплесках трафика, например, во время распродажи билетов на концерт или крупного медийного события. Когда тысячи людей одновременно пытаются совершить действие, сервер начинает «тонуть»: запросы накапливаются в очереди, время ожидания растет, и в итоге пользователи получают ошибки по таймауту [03:16]. В этот момент разработчик встает перед выбором между двумя стратегиями масштабирования:

*   **Вертикальное масштабирование:** покупка более мощного сервера с увеличенным объемом оперативной памяти и мощным процессором. Адриан отмечает, что сервер за 20 долларов в месяц способен обрабатывать миллионы запросов в день [04:20]. Это самый простой путь, не требующий изменений в коде.
*   **Горизонтальное масштабирование:** запуск нескольких идентичных копий приложения на обычных серверах. Это обеспечивает практически неограниченную живучесть системы [05:00].

## ⚖️ Балансировка нагрузки и проблема «состояния»
[[JUMP:02:20]]

Как только в системе появляется более одного сервера, возникает вопрос: как распределять запросы между ними? Для этого в архитектуру вводится **Load Balancer (балансировщик нагрузки)**. Он выступает посредником, который принимает входящий трафик и перенаправляет его на менее занятый сервер [06:31]. В качестве классического примера инструмента для этих целей автор называет **NGINX**, хотя платформы вроде **Vercel** или **Railway** предоставляют встроенную балансировку «из коробки» [06:43].

Однако введение балансировщика порождает новую проблему — проблему управления сессиями. Адриан описывает сценарий «случайного разлогина» [08:13]:

1.  Пользователь авторизуется на Сервере А. Сервер сохраняет заметку о входе в своей оперативной памяти.
2.  При следующем клике балансировщик отправляет запрос на Сервер Б.
3.  Сервер Б «не знает» пользователя, так как его оперативная память пуста, и принудительно просит войти снова [09:43].

Решением становится переход к **Stateless-серверам (серверам без сохранения состояния)**. Ни один сервер не должен хранить секреты о пользователе в своей локальной памяти. Все данные о сессиях выносятся в общее хранилище, доступное всем серверам системы. Чаще всего для этих целей используется **Redis** — высокоскоростное хранилище в оперативной памяти [11:12].

## 🗄️ База данных: пулы соединений и репликация
[[JUMP:12:22]]

Даже если серверная часть масштабирована, общая база данных (БД) может стать узким местом. Автор выделяет две основные проблемы БД. Первая — лимит открытых соединений. Каждое соединение потребляет память, и их количество часто ограничено сотнями или даже десятками на недорогих тарифах [13:19]. Ситуация усугубляется при использовании Serverless-решений (например, на **Vercel**), где резкий всплеск трафика может мгновенно создать сотни новых копий функций, каждая из которых попытается открыть свое соединение и «уронит» базу данными об ошибке подключения [14:10].

Для решения этой проблемы используется **Connection Pooling (пул соединений)** — например, инструмент **PG bouncer** для Postgres. Он поддерживает стабильное небольшое количество открытых линий к БД, которыми серверы «делятся» друг с другом [15:15].

Вторая проблема — исчерпание физической мощности одного устройства. Поскольку в большинстве приложений (таких как TikTok или соцсети) операций чтения в сотни раз больше, чем операций записи, Адриан предлагает использовать **Read Replicas (реплики для чтения)** [17:50]:

*   **Основная база (Primary):** принимает все операции записи.
*   **Реплики:** только для чтения, синхронизируются с основной базой.

Плата за такую скорость — **Replication Lag (задержка репликации)**. Изменения доходят до реплик не мгновенно, и пользователь может не увидеть свой только что опубликованный пост в течение доли секунды [19:09]. Автор подчеркивает: инженер должен сам решать, где допустима такая микро-задержка, а где (как в случае с балансом банковского счета) чтение должно идти только из основного источника [19:47].

## ⚡ Кэширование и оптимизация ресурсов
[[JUMP:20:31]]

Некоторые запросы к базе данных обходятся слишком дорого, чтобы выполнять их постоянно. В качестве примера приводится расчет количества подписчиков у популярного блогера: базе приходится пересчитывать миллионы строк каждый раз, когда кто-то открывает профиль [20:51]. Чтобы избежать этой бессмысленной траты ресурсов, используется **кэширование**.

Результат вычисления сохраняется в быстром хранилище (снова **Redis**). Следующий пользователь получает уже готовое число практически мгновенно [21:56]. Главная сложность здесь — инвалидация кэша, то есть понимание того, когда сохраненный ответ устарел и его нужно удалить [23:13]. Адриан отмечает, что это скорее продуктовое решение, чем техническое: приемлемо ли показывать старое число подписчиков в течение 30 секунд? Для соцсети — да, для банковских операций — нет [24:18].

## ✉️ Очереди задач и фоновые процессы
[[JUMP:25:09]]

Иногда приложение кажется медленным из-за задач, которые сервер пытается выполнить прямо во время ответа пользователю. Например, при регистрации система может отправлять письмо с подтверждением, собирать аналитику и подготавливать данные аккаунта [25:23]. Если внешний сервис рассылки писем работает медленно или временно недоступен, регистрация пользователя «повиснет» или завершится ошибкой [26:16].

Решение заключается в разделении работы:

1.  Сервер создает запись в БД и немедленно отвечает пользователю: «Готово!».
2.  Тяжелая задача (отправка письма) записывается в **Queue (очередь)**.
3.  Отдельный процесс, называемый **Worker (воркер)**, постепенно забирает задачи из очереди и выполняет их в фоновом режиме [28:01].

Для реализации очередей в экосистеме JavaScript часто используется библиотека **BullMQ**, работающая на базе **Redis** [28:26].

## 💎 Шардирование: когда данных слишком много
[[JUMP:30:03]]

Когда объем данных становится настолько огромным, что они физически не помещаются на один диск самого мощного сервера, применяется **Sharding (шардирование)**. Это процесс разделения одной огромной таблицы на части (шарды), распределенные по разным базам данных [32:27].

Чтобы система знала, в какой шарде искать конкретного пользователя, используется фиксированное правило, например, деление ID пользователя на количество шардов и использование остатка от деления [31:48]. Однако шардирование имеет высокую цену:

*   Простые запросы по ID становятся быстрее.
*   Запросы, затрагивающие всех пользователей (например, подсчет общего количества или сортировка по дате), становятся крайне сложными и медленными, так как требуют опроса всех шардов одновременно [34:22].

Адриан приводит в пример компанию **Notion**, которая успешно перешла на шардированную архитектуру, имея более 200 миллиардов строк данных, сохранив при этом работоспособность продукта во время миграции [35:39].

В заключение автор подчеркивает, что проектирование систем — это не просто рисование «коробочек» на схеме, а искусство поиска компромиссов между скоростью, стоимостью и точностью данных [37:33].