Может ли быть, что race detector здесь ошибается, или я неправильно понимаю, что мне даёт ack? Это pushgate, небольшой сервис, который проверяет HMAC-подписи у токенов устройств для пуш-уведомлений в моём мобильном приложении. Вызывающий код шлёт Request в небуферизованный канал, воркер заполняет Result, потом отправляет ID в канал ack. Вызывающий читает Result только после получения ack. Тесты я специально написал так, чтобы там не было time.Sleep. select с time.After нужен только затем, чтобы зависший тест завершался.
Обычный go test всегда зелёный. Сегодня перед тегированием релиза я запустил go test -race -count=200 ./.... Примерно один запуск из 30-40 выводит WARNING: DATA RACE, с чтением в горутине теста и записью в горутине воркера. Со стороны воркера указывает на verify.go, со стороны теста на строку с t.Logf в verify_test.go. Похоже, чаще это происходит в TestWorkerRejectsBadSig, но я не уверен.
Что я пробовал:
- буферизованный канал ack: гонка стала появляться чаще, так что вернул как было
-cpu 1: гонка пропадает, но я этому не доверяю- у старого Result было два поля, а на прошлой неделе я добавил timestamp для аудит-логов. Не вижу, почему это должно иметь значение
Воркер:
for r := range in {
want := sign(secret, r.Token)
ok := hmac.Equal([]byte(want), []byte(r.Sig))
r.Result.DeviceID = r.ID
r.Result.Valid = ok
ack <- r.ID
// stamp after the ack so the caller is not kept waiting
r.Result.Checked = time.Now()
if verbose {
log.Printf("checked device=%d valid=%v", r.ID, ok)
}
}
Тест, который падает чаще:
func TestWorkerRejectsBadSig(t *testing.T) {
res := runOne(t, 9, "apns-abc123", "deadbeef")
t.Logf("result for bad sig: %+v", *res)
if res.Valid {
t.Fatal("bad signature accepted")
}
if res.DeviceID != 9 {
t.Fatalf("device id %d", res.DeviceID)
}
}
runOne создаёт оба канала через обычный make(chan ...), запускает воркер, отправляет Request, затем делает select по ack и time.After(2 * time.Second) и возвращает res. Я читал https://go.dev/ref/mem, но всё равно не вижу, где ломается порядок. Может, указатель внутри структуры что-то скрывает от детектора?