Merion Academy
前往频道在 Telegram
Самое важное про IT на доступном языке. Сотрудничество: @olegpohl Служба поддержки: https://t.me/merion_academy_bot
显示更多8 991
订阅者
+224 小时
-157 天
-630 天
帖子存档
8 991
Пятничная загадка: после релиза часть пользователей видит старую версию сайта
Фронтенд обновили. На компьютере разработчика всё новое, но часть пользователей продолжает видеть старый интерфейс.
Факты:
- новый файл уже есть на сервере;
- проблема возникает не у всех;
- после очистки кэша страница обновляется;
- API отдаёт свежие данные.
Где искать причину?
👀 - в запросе к базе данных
🔥 - в браузерном или CDN-кэше
🤔 - в правах пользователя на сервере
Ставь реакцию. Разбор в следующий понедельник.
8 991
Задачка с собеседования: контейнер работает локально, но падает в CI 🐸
На ноутбуке всё запускается. В CI pipeline контейнер завершается сразу после старта.
Проверять нужно не только код приложения. Сравни переменные окружения, версию образа, права на файлы, сетевые настройки и команду запуска.
На курсе «DevOps-инженер с нуля» эти темы разбираются на практике: Linux, Docker, Docker Compose, CI/CD, Kubernetes, Terraform, Ansible и мониторинг. Сейчас идёт набор на поток. Есть тариф с ментором, ИИ-ассистент для вопросов по материалам и бандл Linux + DevOps.
Начать можно с бесплатной части курса, чтобы оценить программу и формат обучения.
➡️ Программа, набор на поток и бесплатная часть
8 991
Контейнер сразу завершается после запуска: что проверить 🐸
Команда
docker ps показывает пусто, хотя минуту назад контейнер запускался.
Проверь по шагам:
docker ps -a
Покажет остановленные контейнеры и их статус.
docker logs <container_id>
Покажет, почему завершился основной процесс.
docker inspect <container_id>
Поможет проверить команду запуска, переменные окружения и код выхода.
Контейнер живёт, пока живёт его основной процесс. Если команда внутри завершилась, контейнер тоже остановится.8 991
Логи не нужно читать целиком: начни с пяти строк вокруг ошибки 🐸
Когда сервис упал, найди сначала время и ключевое слово, потом уже контекст.
grep -n "ERROR" app.log | tail -n 5
Посмотреть последние ошибки проще, чем листать весь файл.
tail -n 100 app.log
Посмотреть последние 100 строк и понять, что происходило перед ошибкой.
journalctl -u nginx --since "10 minutes ago"
Если сервис работает через systemd, сразу ограничь поиск нужным временем.
Логи дают ответ только тогда, когда в них есть время, уровень ошибки и контекст запроса.8 991
Разгадка: почему зелёные метрики не означают, что всё работает
Правильный ответ: 🔥 смотреть нужно не только на средние показатели, а на ошибки и
p99 конкретного сценария.
Если 99% оплат проходят быстро, среднее время ответа останется красивым. Но один процент пользователей может получать таймаут или ошибку на шаге оплаты.
Порядок проверки:
1. найти endpoint, который отвечает за оплату;
2. сравнить долю ошибок до и после релиза;
3. посмотреть p95 и p99 отдельно для этого endpoint;
4. открыть трассировки неуспешных запросов;
5. проверить, не падает ли один из внешних сервисов.
Мониторинг должен показывать не только состояние серверов, но и то, могут ли пользователи выполнить важное действие.8 991
Пятничная загадка: все метрики зелёные, но пользователи не могут оплатить заказ
После небольшого релиза:
- CPU и память в норме;
- среднее время ответа не изменилось;
- ошибок в логах почти нет;
- пользователи пишут, что оплата иногда не проходит;
- проблема возникает примерно у 1% заказов.
Что проверишь первым?
👀 - график среднего времени ответа
🔥 -
p99 и ошибки по endpoint оплаты
🤔 - загрузку CPU на сервере базы данных
Ставь реакцию. Разбор в следующий понедельник.8 991
Задачка с собеседования: сервис работает, но после релиза стал медленным 🐸
CPU не загружен, память в норме, ошибок в логах почти нет. Пользователи говорят, что страницы открываются дольше обычного.
С чего начнёшь проверку?
Сначала сравни метрики до и после релиза, затем посмотри
p95 и p99, ошибки по конкретным endpoint и трассировку медленного запроса. Среднее время ответа здесь может выглядеть нормально.
Такие ситуации разбирают на курсе «DevOps-инженер с нуля». В программе есть Linux, Docker, Kubernetes, CI/CD, мониторинг, Terraform и Ansible. На странице также доступен комплект Linux + DevOps для тех, кто хочет начать с базы.
Можно начать с бесплатной части курса и посмотреть, как устроено обучение.
➡️ Программа и бесплатная часть8 991
Среднее время ответа 200 мс, а пользователи всё равно ждут 8 секунд 🐸
Среднее значение может скрыть самые медленные запросы.
Представь 1000 запросов:
- 970 уложились в 200 мс
- 20 заняли около секунды
- 10 выполнялись 8-10 секунд
Среднее будет выглядеть неплохо, но каждый сотый пользователь получит очень медленную страницу.
Поэтому смотрят на перцентили:
p50 показывает типичный запрос.
p95 показывает границу для самых медленных 5%.
p99 показывает границу для самых медленных 1%.
Если среднее зелёное, а p99 растёт, проблема уже есть. Просто среднее её прячет.8 991
Кто поменял эту строку и зачем нужен `git blame` 🐸
Нашёл подозрительный фрагмент кода, но не знаешь, откуда он взялся?
git blame -L 42,55 src/app.js
Команда покажет, какой коммит и какой автор изменили строки с 42 по 55.
Дальше открой сам коммит:
git show <commit_hash>
Так можно увидеть не только автора, но и соседние изменения, сообщение коммита и первоначальную задачу.
git blame не отвечает на вопрос «кто виноват» автоматически. Он помогает быстро найти контекст, когда строка стала проблемной.8 991
Три команды, когда сервер внезапно начал тормозить 🐸
Не открывай сразу десять вкладок. Начни с трёх быстрых проверок.
uptime - Показывает нагрузку и помогает понять, сервер тормозит давно или проблема появилась только что.
free -h - Проверяет оперативную память и swap. Если свободной памяти почти нет, процессы могут начать ждать диск.
df -h - Показывает заполненность файловых систем. Когда заканчивается место, начинают странно вести себя логи, базы и сервисы.
После этого уже смотри конкретный процесс и логи, иначе легко чинить не причину, а первый симптом.
Какую команду запускаешь первой?8 991
Разгадка: почему SQL-отчёт показал 4000 ₽ вместо 1000 ₽
Правильный ответ: 🔥 два
JOIN создали четыре строки для одного заказа.
У заказа №42 было:
- 2 строки в order_items
- 2 строки в payments
После соединения таблиц база получила 2 × 2 = 4 строки. В каждой из них осталось o.total = 1000, поэтому SUM(o.total) сложил одну и ту же сумму четыре раза.
orders не содержит дублей. Дубли появились в результате соединения таблиц.
SUM(DISTINCT o.total) здесь не является надёжным исправлением. Если два разных заказа стоят одинаково, один из них потеряется в подсчёте.
Если нужна выручка по заказам, не присоединяй таблицы «один ко многим» до подсчёта суммы:
SELECT o.id, o.total AS revenue
FROM orders o;
А если нужны товары и платежи, сначала собери их отдельно по order_id, а потом присоедини к заказу.8 991
Пятничная загадка: SQL-отчёт умножил выручку
В таблице
orders у заказа №42 сумма 1000 ₽. У него две строки в order_items и два события в payments. Аналитик собирает отчёт:
SELECT o.id, SUM(o.total) AS revenue
FROM orders o
JOIN order_items i ON i.order_id = o.id
JOIN payments p ON p.order_id = o.id
GROUP BY o.id;
Для заказа №42 отчёт показывает 4000 ₽. В таблице orders одна строка, сумма там правильная. Ни одна таблица не содержит дублей по своему первичному ключу.
Почему получилось именно 4000 ₽?
👀 - SUM складывает значение total дважды: один раз до JOIN и ещё раз после
🔥- два JOIN создали четыре строки: 2 товара × 2 платежа
🤔- база округлила сумму из-за разных типов данных в таблицах
Ставь реакцию. Разбор и правильный запрос — в следующий понедельник.8 991
Найти пользователей с шестью и более заказами — куда поставить условие? 🐸
SELECT user_id, COUNT(*) AS orders_count
FROM orders
GROUP BY user_id
HAVING COUNT(*) >= 6;
WHERE фильтрует строки до группировки. HAVING фильтрует группы после неё. Поэтому условие на COUNT(*) находится в HAVING.
Если нужны только оплаченные заказы, добавь условие раньше:
SELECT user_id, COUNT(*) AS paid_orders
FROM orders
WHERE status = 'paid'
GROUP BY user_id
HAVING COUNT(*) >= 6;
Строчка WHERE status = 'paid' убирает неоплаченные заказы до подсчёта. А HAVING оставляет пользователей, у которых оплаченных заказов шесть или больше.
Лайфхак для проверки: сначала запусти тот же запрос без HAVING и посмотри на суммы по нескольким знакомым user_id.8 991
Если запрос из Node.js зависает слишком долго, то ставим дедлайн
Если проект использует встроенный
fetch, укажи таймаут через AbortSignal.timeout:
const res = await fetch(url, {
signal: AbortSignal.timeout(5000)
});
Через 5 секунд сигнал отменит запрос. Ошибку всё равно нужно обработать: таймаут — ожидаемый сценарий при работе с сетью, а не повод ронять весь обработчик.
try {
const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
if (!res.ok) throw new Error('HTTP ' + res.status);
} catch (err) {
console.error('Запрос не выполнен:', err);
}
Важно: таймаут ограничивает ожидание на стороне клиента; он не обещает, что сервер прекратит уже начатую работу.
Есть ли таймауты у внешних API, от которых зависит ваш сервис?8 991
Разгадка пятничного кейса 🐸
Одна из вероятных причин – в зависимости
useEffect попал объект или функция, которые создаются заново на каждом рендере. Эффект делает запрос → ответ меняет state → новый рендер создаёт новую зависимость → эффект снова делает запрос.
Проверка: сравнить значения зависимостей между рендерами и временно заменить составную зависимость на стабильный примитив, например status или page. Если цикл исчез, источник найден. В реальном проекте возможны и другие причины; одних 150 запросов для точного диагноза мало.
А теперь короткая ловушка Python:
def add(x, items=[]):
items.append(x)
return items
Что вернут подряд add(1) и add(2)? Список по умолчанию создаётся один раз при определении функции: ответы будут [1] и [1, 2].
Безопасный шаблон:
def add(x, items=None):
if items is None: items = []
items.append(x)
return items
И в React, и в Python - всё решает момент создания значения.8 991
React отправляет запрос дважды? Сначала проверь причину 🐸
В разработке
StrictMode может дополнительно запускать setup и cleanup эффекта. Это помогает заметить эффекты, которые нельзя безопасно повторить. В production такое дополнительное выполнение из-за StrictMode не происходит.
Но если запрос повторяется при каждом рендере, посмотри на зависимости:
const filters = { status: 'open' };
useEffect(() => { load(filters); }, [filters]);
Объект filters создаётся заново при каждом рендере. Для React это новая зависимость. Вынеси постоянное значение за пределы компонента или используй примитивные зависимости:
useEffect(() => { load(status); }, [status]);
И ещё: эффекту с запросом полезна отмена через AbortController, чтобы старый ответ не перезаписал свежий.
Сохрани пост на случай, когда в Network вдруг появятся одинаковые запросы. В пятницу вернёмся к этой теме уже в формате расследования.8 991
Пятничная загадка: кто размножил запросы?
После небольшой правки React-страница стала открываться 8 секунд. Backend отвечает за 50 мс, база — за 20 мс, ошибок нет. В DevTools - около 150 одинаковых запросов к
/api/items.
Факты расследования:
• проблема воспроизводится и в production-сборке;
• запрос запускается внутри useEffect;
• массив зависимостей эффекта заполнен;
• после каждого ответа компонент обновляет состояние;
• если убрать обновление состояния, шквал запросов прекращается.
Почему запрос запускается снова и снова?
👀 - виноват StrictMode: он дублирует запросы и в production
🔥 - в зависимостях эффекта есть объект или функция, которые создаются заново при каждом рендере
🤔 - браузер автоматически повторяет запрос, пока не получит ответ из кэша
Разгадка - в понедельник.8 991
Задачка с собеса: почему список в React «путает» состояние строк? 🐸
Есть список задач. У каждой строки – поле ввода. Ты сортируешь список, и текст из первой строки внезапно оказывается у другой задачи. Данные в API правильные. Где ошибка?
Частая причина – key={index}. После сортировки индекс остался на позиции, а задача на этой позиции уже другая. React переиспользовал компонент и его локальное состояние.
Исправление: key={task.id} – стабильный идентификатор самой задачи. Он должен быть уникален среди соседних элементов и не меняться при сортировке.
На собеседовании за этим обычно спрашивают дальше: где хранить состояние, когда нужен useEffect, как обрабатывать формы и запросы к API. Эти темы разбираются на курсе «Frontend-разработка на React»: компоненты, JSX, состояние, события и работа с API.
➡️ Программа курса и бесплатная часть по ссылке
