Race detector
Тема дорожной карты · Golang
Race detector в Go — встроенный инструмент динамического анализа, обнаруживающий гонки данных — конкурентные несинхронизированные обращения к разделяемой памяти, где хотя бы одно из них является записью, — путём инструментирования Golang-бинарника отслеживанием теневой памяти на этапе компиляции с флагом -race. Его включают командами go test -race ./... для тестовых наборов или go run -race main.go для ad-hoc проверок; рантайм затем выводит подробный отчёт, включая трассировки стека goroutines, когда race detector обнаруживает конфликтующие обращения. Race detector перехватывает каждое чтение и запись в память и сравнивает их с вектором времени для каждого адреса, обнаруживая гонки с участием словарей, слайсов, полей структур и интерфейсных значений, которые не были должным образом защищены sync.Mutex, sync.RWMutex или каналами. Запуск race detector в CI на каждом pull request — стандартная практика для Golang-проектов, поскольку гонки данных вызывают периодические, трудновоспроизводимые баги в продакшене: повреждение данных, паники или незаметно неверные результаты. Накладные расходы race detector (~5–10× медленнее, ~5–10× больше памяти) делают его непригодным для продакшена, но незаменимым в staging-средах наряду с профилированием pprof и трассировкой OpenTelemetry.
Как это работает
Race detector построен на goroutines (лёгкие green threads, мультиплексируемые на OS-потоки Go-runtime) и channels (типизированные каналы для сообщений между goroutines). go funcName() запускает goroutine; ch <- value шлёт; value := <-ch принимает. select ждёт на нескольких каналах; context.Context несёт cancellation + дедлайны через дерево вызовов. sync.Mutex, sync.WaitGroup, sync.Once, атомарные примитивы, errgroup для группировки goroutines.
Когда применять
Goroutines используйте свободно — дёшевы (~2-4KB stack начально, растёт по нужде). Всегда имейте способ остановить goroutine (cancellation context.Context, close канала, deadline) — утёкшие goroutines = тихий рост памяти. Context первым аргументом — в любую функцию с I/O. sync.Mutex — для shared state, когда channels неудобны. errgroup — для "fan out, wait, первая ошибка пробрасывается".
Типичные ошибки
Ловушки Race detector: утечки goroutines (goroutine ждёт навеки на канале, в который никто не пишет); data race на shared map без mutex (race detector ловит: go test -race); deadlock (go test -race НЕ ловит deadlock — осторожно); не пробрасывают context.Context, и cancellation не доходит до глубокого вызова.