-race в CI и go vet
Команда встроила -race в CI с остановкой на первой гонке и добавила go vet на каждый коммит. Первый же прогон vet нашёл в чужом коде WaitGroup, переданный по значению. Этот баг раньше вешал сервис навсегда.
race detector встраивают в CI с GORACE=halt_on_error=1, а go vet отдельно ловит копирование блокировок — дешёвую проверку, которую можно гонять на каждом коммите.
-race в CI
# упрощённый шаг CI
run: go test -race ./...
env:
GORACE: halt_on_error=1
GORACE=halt_on_error=1 останавливает тесты на первой же гонке — без этого go test -race продолжит прогон, и гонка утонет в выводе. Детектор в CI — это страховка: гонка, проскочившая локально, падает в пайплайне и блокирует merge.
Go vet — дешёвый сосед
func work(wg sync.WaitGroup) { // баг: WaitGroup по значению
wg.Add(1)
go func() { defer wg.Done(); task() }()
wg.Wait()
}
go vet ловит копирование блокировок — copylocks: передачу sync.Mutex, WaitGroup и прочих noCopy-типов по значению. Заметь: copylocks ловит именно полный go vet, а go test исполняет только малую часть анализаторов vet, и copylocks в неё не входит. Это именно тот баг из, который вешает Wait навсегда. Vet ловит его на этапе компиляции — дёшево, до всяких прогонов.
Полный набор
Итого дешёвый стандартный набор: go vet./... на каждый коммит, go test -race./... в CI. Одно ловит структурные ошибки с блокировками, другое — реальные гонки на пройденных путях. Вместе они закрывают почти все «невидимые» баги конкурентности до продакшена.
Вы узнаете:
- как
noCopy-механизм сообщает vet о запрете копирования - какие репортеры есть у
GORACE
Проверьте себя
Почему go vet — на каждый коммит, а go test -race — только в CI?
Кто ловит копирование sync.WaitGroup (copylocks)?
Что делает GORACE=halt_on_error=1?
правильных ответов
CI: go test -race с GORACE=halt_on_error=1; каждый коммит: go vet с copylocks. Две дёшево автоматизируемые проверки конкурентности.
10 000 ₽ · 60 минут · онлайн