Как одна осиротевшая go-рутина научила меня искать проблему не в том, что работает, а в том, что подозрительно молчит.
Всё должно было закончиться на финальных титрах. Фильм досмотрен, Apple TV выключен, TorrServer на удалённом iMac должен был просто замереть в ожидании следующего сеанса. Но кулер старого сервера начал нарастать по оборотам, переходя в ровный, тревожный гул.
Заглянул в мониторинг: один поток процессора выжжен в ноль. 104% нагрузки. Постоянно. Часами. При этом в интерфейсе сервера тишина - активных закачек нет, торренты висят в статусе «в базе», трафик нулевой.
Лечил здоровую сеть
Первым делом полез в сетевой стек. Подумал, что какой-нибудь кривой пир зациклил запросы или туннель решил устроить DDoS-атаку на самого себя. Проверил логи сетевого шлюза - чисто. Пакеты не летают, интерфейсы спят.
Потом решил, что дело в самом движке торрентов - может, завис какой-то внутренний процесс перехеширования данных? Полез в API сервера, пытался принудительно сбросить кэш и удалить торренты из списка. Но API отвечал корректно, а загрузка CPU даже не шелохнулась.
Самое странное было в логах самого TorrServer. Я ожидал увидеть там бесконечный поток ошибок, панику или хотя бы предупреждения о переполнении буфера. Но лог был мёртв. После строки Torrent close by timeout и закрытия кэша не появилось больше ни единого символа.
Система рапортовала, что всё в порядке. Процессор при этом пытался пробить потолок. Это была идеальная ловушка: приборы показывают «зелёный», а железо раскаляется.
Когда молчание - это симптом
Когда логика «ищи ошибку в тексте» зашла в тупик, я скормил ситуацию ИИ. Не просил починить код - попросил помочь интерпретировать эту странную тишину.
И тут пришёл вопрос, который перевернул диагностику:
«А что если тишина в логе - это не признак покоя, а симптом того, что поток зациклился внутри функции, которая вообще не умеет писать в лог?»
Это был момент истины. Я слишком привык, что любая проблема оставляет след в текстовом файле. Но если горутина (легковесный поток в Go) «осиротела» и застряла в бесконечном цикле ожидания закрытого канала или пустом спинере, она будет жрать процессор, не произнося ни слова.
Оказалось, это известный баг движка anacrolix/torrent. После разрыва соединения и таймаута одна из горутин иногда просто забывает выйти. Она крутится вхолостую, перепрыгивая с одного системного потока на другой, создавая иллюзию нагрузки, но не совершая никакой полезной работы. Она была жива, но абсолютно нема и бесполезна.
Сторож для немого призрака
Лечить такой баг в чужом бинарнике без исходников - затея для мазохистов. Проще было создать внешний «надсмотрщик», который бы понимал, когда тишина в логах становится подозрительной.
Я написал простой watchdog на bash и обернул его в launchd. Логика простая: сервис считает «сердцебиением» периодические системные сообщения (LPD-эхо), которые приходят каждые 5 минут. Если лог молчит дольше 5.5 минут, а CPU при этом всё ещё зашкаливает за 80% - значит, мы поймали того самого «призрака».
# Фрагмент логики watchdog.sh
if [[ $active_torrents -eq 0 ]] && [[ $cpu_usage -gt 80 ]]; then
launchctl kickstart -k gui/501/com.torrserver
fi
Сторожевой пес теперь просто перезапускает сервис, когда тот уходит в «безумный сон». TorrServer поднимается мгновенно, торренты остаются в базе, а я больше не слышу, как кулер iMac пытается улететь в стратосферу после каждого фильма.
Самое громкое - в тишине
Этот кейс оставил одну мысль - про доверие к логам. Мы привыкли считать их единственным источником правды. Но лог - это то, что разработчик захотел нам рассказать.
Молчание лога при явном симптоме (нагрев, нагрузка) - это самый громкий сигнал о том, что проблема лежит глубже уровня приложения. Она лежит на уровне управления потоками и памяти, там, где абстракции начинают течь.
ИИ и здесь сработал не подсказкой, а сменой оптики: помог увидеть моё же допущение: «если в логах пусто, значит ничего не происходит». Оказалось, что самое страшное часто происходит именно в этой пустоте.
