Go-разработчик
80 задач. Условие и варианты ответа открыты всем; проверка, подсказка и разбор — в клубе.
Основы Go: типы, ошибки, интерфейсы
- Что вернёт чтение отсутствующего ключа из map с двумя переменными
- Порядок выполнения нескольких defer в одной функции
- Почему счётчик в структуре не меняется после вызова функции
- append в подсрез изменил элементы исходного среза
- errors.Is не находит sentinel-ошибку после форматирования
- Запись в nil map роняет сервис, а чтение — нет
- Функция вернула nil-указатель, а проверка err != nil сработала
- Тип не удовлетворяет интерфейсу из-за указательного получателя
Конкурентность: горутины, каналы, context
- Чтение из закрытого и вычитанного канала
- Забытый wg.Done() в обработчике батча
- Отправка в небуферизованный канал в той же горутине
- HTTP-хендлер строит контекст от Background вместо контекста запроса
- Инкремент общего счётчика из тысячи горутин
- Несколько продюсеров закрывают один и тот же канал
- Утечка горутин при выходе по таймауту из select
- Промах кэша вызывает лавину одинаковых запросов в базу
PostgreSQL и Redis: запросы и кэш
- Зачем EXPLAIN ANALYZE, если план уже виден в EXPLAIN
- Список заказов отдаётся секунду из-за запроса в цикле
- Пользователи видят старый профиль после успешного сохранения
- Индекс по колонке не помогает запросу с функцией в условии
- Составной индекс не ускорил выборку по второй колонке
- HTTP-вызов внешнего провайдера внутри открытой транзакции
- Планировщик игнорирует новый индекс на малоселективной колонке
- Распределённый лок в Redis снимается чужим владельцем
Kafka и надёжность интеграций
- Пять консьюмеров в группе на три партиции топика
- События одного заказа приходят потребителю в перепутанном порядке
- Одно битое сообщение остановило всю партицию
- Offset фиксируется до обработки сообщения
- Событие потерялось между коммитом в базу и отправкой в Kafka
- Повторы без выдержки добивают восстанавливающийся сервис
- Повтор запроса на оплату списывает деньги дважды
- После деплоя часть сообщений теряется на завершении подов
HTTP-клиенты, серверы и gRPC
- Провайдер SMS завис, и число горутин сервиса выросло до сотен тысяч
- Под нагрузкой сервис упирается в лимит открытых файлов
- В JSON-ответе ручки отсутствует поле с суммой заказа
- Клиент отправил поле с опечаткой, получил 200, а настройка не сохранилась
- Сервис перестаёт отвечать, когда клиенты открывают соединения и медленно шлют заголовки
- Паника в фоновой отправке письма роняет весь HTTP-сервис
- gRPC-вызов сервиса остатков висит, пока не закончится терпение пользователя
- Клиент бесконечно повторяет запрос несуществующего заказа
- Идентификатор пользователя в контексте затёрся значением из чужого пакета
- Добавили реплики gRPC-сервиса, а вся нагрузка по-прежнему идёт на один под
- Выгрузка каталога в JSON поднимает память пода на полтора гигабайта
- При 300 запросах в секунду к одному сервису копятся тысячи соединений TIME_WAIT
Контейнеры, Kubernetes и эксплуатация сервиса
- Образ сервиса на Go весит 1,1 ГБ, хотя бинарник — 18 МБ
- В образе FROM scratch запросы к платёжному API падают с ошибкой x509
- При каждом деплое часть запросов к сервису обрывается
- Логи сервиса в Kibana невозможно отфильтровать по номеру заказа
- В контейнере time.LoadLocation("Europe/Moscow") возвращает ошибку
- Под с лимитом в одно ядро на 64-ядерном узле упирается в CPU-троттлинг
- Сервису хватает 300 МБ, но под с лимитом 512 МиБ периодически получает OOMKilled
- На публичном адресе сервиса открывается /debug/pprof
- Во время инцидента никто не может сказать, какой коммит работает в проде
- Даже с srv.Shutdown при деплое остаются ошибки 502 на балансировщике
- После srv.Shutdown процесс завершается, а воркер не дописал пачку в базу
- Отставание консьюмера растёт до часа, а автомасштабирование не добавляет подов
Тестирование: testing, testify, httptest, testcontainers
- go test сообщает «no tests to run», хотя тест в файле есть
- Шесть копий теста валидации телефона отличаются одной строкой
- Упавший тест вместо понятной ошибки выдаёт панику nil pointer
- Интеграционный тест показывает (cached), хотя данные в базе изменились
- Тесты кэша зелёные, а в проде изредка паникует concurrent map writes
- Сервис уведомлений невозможно протестировать без реального SMTP-сервера
- Тест фонового воркера ждёт результат через time.Sleep(2 * time.Second)
- Тест сравнения списка категорий падает примерно в каждом третьем запуске
- Как протестировать клиент платёжного провайдера, включая ошибки 500 и таймауты
- Каждый интеграционный тест репозитория поднимает свой контейнер PostgreSQL
- Тест JSON-отчёта на 400 строк держит ожидаемый результат строковой константой
- Бенчмарк показывает, что новая сериализация в 20 раз медленнее, хотя это не так
Наблюдаемость: метрики, трассировка, pprof
- Счётчик заказов объявлен, а Prometheus не видит ни одной метрики сервиса
- В логах тысячи записей «failed to save», и непонятно, что именно не сохранилось
- Какие метрики HTTP-сервиса завести в первую очередь
- Каждый запрос несуществующего товара пишет в лог ERROR, и алерты молчат по-настоящему важному
- Метрика времени ответа с меткой path раздула память Prometheus
- Перцентиль p99 по всем подам не получается из Summary-метрик
- В Jaeger трасса запроса заканчивается на сервисе заказов, хотя дальше идёт вызов в оплату
- Метрика go_goroutines растёт с 300 до 80 тысяч за сутки
- Сервис каталога потребляет втрое больше CPU после релиза, а изменений в логике немного
- Спаны параллельных запросов к складам не привязаны к трассе заказа
- Память сервиса растёт неделю, а профиль heap показывает в основном json
- Дашборд показывает p99 ровно 1 секунду, хотя пользователи ждут по 4 секунды
