Pull to refresh

Comments 12

Подробности того, как ловил ошибку, как раз и есть самое интересное.

А ещё это показательный пример работы с нейронкой (grok, в моём случае). Если скормить ей кусок кода из статьи и попросить найти проблемы, то она напишет кучу воды про обработку ошибок и т.п., но не про это. А если прямо спросить про гонку данных, то распишет во всех подробностях. Вот поэтому нейронки не заменят программистов, по крайней мере не в близкой перспективе. Потому что основной скилл хорошего программиста - это вовсе не умение что-то там написать. Это понимание того, что надо написать, в какую сторону копать и какие вопросы спрашивать. В том числе и у нейронки.

Подробности того, как ловил ошибку, как раз и есть самое интересное.

Понятное дело. Только пересказать это невозможно.

Сначала пытался отсекать лишнее. Но только беда в том, что как я не упрощал этот код, плавающая ошибка не уходила (может это и к лучшему, иногда бывает так, что плавающая ошибка всё время лезет, пока ей не займешься. А потом прячется).

Выяснил, что если вставить задержку в гороутинку, которая делает ServeTLS, то ошибка выскакивает всегда. А если в другую, то и вовсе не выскакивает.

С самого начала было понятно, что это какая-то гонка. Но сколько я ее не искал, найти не мог.

Посмотрел в код стандартной библиотеки, не делает ли она чего в фоне. Вроде нет, но она сложная, её сложно читать, поэтому 100% не было.

Возникла гипотеза, что может если создавать сервер и сразу делать к нему запрос, возможно внутри самого сервера что-то не успело еще проинициализироваться, и слишком ранние запросы сервер отрабатывает неправильно (в боевых-то условиях так редко бывает и особенно не заметно). Написал соответствующий тест, погонял - все ОК. Догадка не подтвердилась.

Поискал, где запускаются гороутинки в net.http и crypto.tls. Сравнил между версиями go. Никакой значимой разницы не обнаружил.

Потом пришла догадка, что может Serve и ServeTLS и не стоило бы одновременно запускать. Посмотрел еще раз попристальнее в код net.http - вижу, что они там на старте чего-то в Server накручивают под себя, в его глубинах. Так догадка перерасла в уверенность.

Ну а дальше просто, разбил Server на два, проверил - работает. Убрал всю вставленную в процессе отладки отладочную печать, еще раз проверил - работает. Ну и туды его, в commit :)

Там вроде есть какая-то синхронизация, в инициализации этого самого http.Server. Т.е., там не классическая гонка, а "кто первый встал, того и тапки". Т.е., в зависимости от того, какая функция первая успеет прошмыгнуть в критическую секцию, результат получится разный. Но консистентный. Но при этом не подходящий для другой функции.

Да не похоже на гонку. Тем более тогда бы ломался бы не "всегда ServeTLS". "unexpected EOF" в HTTP намекает на то, что сервер некорректные данные отдаёт, например в TLS соединении - обычные незашифрованные.

Вполне себе гонка: две горутины инициализируют одни и те же приватные поля структуры

Правда не уверен, что test -race такое ловит

race может и не ловит, а вот линтер из vscode вполне. Но при гонке должна какая-то рандомизация быть. Т.е. иногда срабатывало бы наоборот, TLS работает, а в plain - шифрованная каша.

race может и не ловит, а вот линтер из vscode вполне.

А Вы попробовали, или это - теоретическое умозаключение?

Т.е. иногда срабатывало бы наоборот, TLS работает, а в plain - шифрованная каша.

Там инициализируются какие-то внутренности, которые важны для TLS, а для plain не очень важны. Поэтому plain не очень ломается. Ну или, по крайней мере, мне в ограниченном числе тестов не удались заставить его сломаться.

А Вы попробовали, или это - теоретическое умозаключение?

Точных подробностей не назову, пол года уже прошло, но это было со мной. Защищал карту стандартным sync.Mutex, линтер ругнулся в какой-то момент, мол у вас потенциальный race condition. Был абсолютно уверен, что это ложное срабатывание, ведь я умею писать код с потоками. ;-)

Но варнинги висячие не люблю, полез разбираться что конкретно ему не нравится и обнаружил, что прав был таки линтер, а не я, был вариант, при котором шло незащищённое обращение к карте.

Ну или, по крайней мере, мне в ограниченном числе тестов не удались заставить его сломаться.

Ну, возможно количество тестов было очень ограниченным. Просто признак реальной гонки - когда результат непостоянен, то один, то другой. Собственно весь смысл гонки в этом.

Результат был непостоянен, я ж написал

Значит проглядел, тогда да, гонка.

Sign up to leave a comment.

Articles