Node.js-разработчик
80 задач, из них 10 на код с автопроверкой. Условие и варианты ответа открыты всем; проверка, подсказка и разбор — в клубе.
Асинхронность и event loop
- Порядок вывода: синхронный код, микрозадача и таймер
- Синхронный тяжёлый расчёт в HTTP-обработчике и параллельные запросы
- Забытый await: что попадёт в лог
- Promise.all: один упавший вызов роняет весь ответ
- try/catch вокруг колбэка таймера не ловит исключение
- Отдача больших файлов: буфер в памяти против потока
- CPU-bound задача в веб-процессе: куда её вынести
- 3000 внешних вызовов: цикл с await против Promise.all на всё сразу
REST API на NestJS и Express
- Какой ответ вернуть на успешное создание ресурса
- Аутентифицирован, но не имеет прав: какой код ответа
- Nest can't resolve dependencies: провайдер не экспортирован из модуля
- Детали ошибки БД утекают клиенту в ответе 500
- TypeScript-тип на @Body() не проверяет тело запроса
- Переименование поля в ответе сломало мобильные приложения
- Деградация платёжного провайдера кладёт весь сервис
- WebSocket на нескольких репликах: сообщение доходит не всем
PostgreSQL, ORM и кэш Redis
- Seq Scan на пяти миллионах строк в EXPLAIN
- N+1 запрос в ORM при выводе списка заказов
- Изменение схемы БД: миграция в репозитории против ручного ALTER
- Индекс есть, а запрос его не использует
- Потерянное обновление баланса при параллельных списаниях
- После обновления товара клиенты час видят старую цену
- Добавили индекс — запись замедлилась, а выборка не ускорилась
- Истёк TTL горячего ключа — лавина запросов в базу
Очереди, надёжность и эксплуатация
- Отправка писем прямо в HTTP-обработчике
- Пароль от базы в docker-compose.yml в репозитории
- Сборка образа подтянула не ту версию зависимости
- Воркер получил одно и то же сообщение дважды
- Пайплайн выкатывает релиз при красных тестах
- Среднее время ответа 120 мс, а пользователи жалуются на тормоза
- Событие в брокер после коммита: почему теряются доменные события
- Ядовитое сообщение крутит очередь по кругу
Потоки, память и производительность процесса
- Обработчик читает файл шаблона через readFileSync на каждом запросе
- На сервере восемь ядер, а сервис упирается в 100% одного ядра
- Человекочитаемый размер загруженного файла
- После обрывов скачивания у сервиса кончаются файловые дескрипторы
- Экспорт миллиона строк в CSV съедает гигабайты памяти процесса
- Импорт каталога JSON-телом на 80 МБ подвешивает сервис на несколько секунд
- Память процесса растёт неделю, пока под не убивают за превышение лимита
- Ограниченный кэш с вытеснением давно не использованных записей
- Как понять, что сервис тормозит из-за блокировок event loop, а не из-за базы
- Чтобы сервис не падал, в uncaughtException добавили логирование и продолжение работы
- При деплое процесс не завершается по SIGTERM и убивается через 30 секунд
- Потоковый разбор NDJSON из кусков произвольной длины
TypeScript на бэкенде: данные из базы, конфиг и события
- Сумма заказа с доставкой получилась «990.00150» вместо 1140
- Опечатка в имени поля вебхука прошла компиляцию и сломала начисление
- Сервис стартовал без ошибок, но слушает случайный порт
- Перевод числовых колонок из строк драйвера в числа
- Эндпоинт профиля возвращает поле passwordHash
- Сервис-клиент падает на createdAt.getTime is not a function
- В базе появился статус refunded, а сервис молча показывает заказы как «новые»
- findOne вернул null, а компилятор считал результат пользователем
- Разбор и проверка переменных окружения при старте
- Интерфейс DTO и схема валидации разошлись, и поле проверяется не так, как типизировано
- Обработчик событий заказа обращается к полю reason у события оплаты
- Проверка события заказа из брокера без исключений
Тестирование: Jest, Supertest, моки и тестовая база
- Тест создания заказа зелёный, хотя сервис возвращает ошибку
- Интеграционные тесты API падают с EADDRINUSE при параллельном запуске
- Тесты прошли, но Jest не завершается и пишет «did not exit one second after»
- Тест повторной отправки вебхука через 30 секунд идёт 30 секунд
- Интеграционные тесты сервиса доставки ходят в песочницу партнёра и падают по утрам
- Тесты с базой проходят по одному файлу и падают при полном прогоне в четыре воркера
- Второй тест в файле получает цену, закэшированную в первом
- Снапшот ответа API ломается на каждом прогоне из-за даты и идентификатора
- Замена дат и UUID метками для стабильного снапшота
- Фабрика jest.mock падает с ReferenceError на переменной, объявленной выше
- Каждый тестовый файл поднимает свой контейнер PostgreSQL и применяет миграции
- Фейковые часы для тестирования таймеров
WebSocket, SSE и real-time
- Страница статуса доставки опрашивает сервер каждую секунду
- Для уведомлений только от сервера к клиенту выбрали WebSocket с кучей своего кода
- Токен доступа для WebSocket передаётся в адресе подключения
- WebSocket-соединения рвутся ровно через 60 секунд тишины
- Подменив номер заказа в сообщении подписки, пользователь видит чужую доставку
- После переподключения доска заказов не показывает события за время обрыва
- На каждое изменение одного заказа сервер рассылает список из трёх тысяч заказов
- Реестр подписок клиентов на комнаты для рассылки
- Разбор одного сообщения Server-Sent Events
- Несколько клиентов на плохом интернете раздувают память WebSocket-сервера
- Socket.IO на трёх репликах сыплет ошибками «Session ID unknown»
- После каждого деплоя 40 тысяч клиентов одновременно переподключаются и кладут сервис
