Стендап Сьогодні 📢 Канал в Telegram @stendap_sogodni

🤖🚫 Контент вільний від AI. Цей пост на 100% написаний людиною, як і все на моєму блозі. Насолоджуйтесь!

18.10.2023

Профілювання проблемної ситуації з Go

Поступила скарга: наш сервіс на Go, локально запущений в Докері, призводить до постійного споживання CPU, навіть коли нічого не робить. Що сталося? Чи це Докер винний? Така задача розвʼязується профайлером.

По-перше, сам Docker командою docker top вкаже, що CPU споживає нібито сам процес сервісу. Та й пояснення від найпростішого вказує туди ж.

Щоб зрозуміти, що робить сервіс, підключаємо профайлер. В такій ситуації — на всю програму разом. Для цього в Go достатньо додати три рядки на початок функції main():

f, _ := os.Create("/mnt/tmp/service.prof")
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()

Це автоматично покриє весь код, включаючи горутіни. Компілюємо, запускаємо контейнер з сервісом, чекаємо пару хвилин та зупиняємо. Це згенерує профіль та збереже його в файл (в Докері зручно буде, якщо файл помістити в директорію, що примонтована — бо тоді відразу можна з тим файлом працювати.)

Далі можна слідувати офіційному посібнику. Я раджу не блукати в консольних командах профайлера, а відкривати вебверсію (команда web). Відразу бачу, що більшість часу програма витрачає на компонент, який оновлює деякий кеш. Ну трохи дивно, але має сенс, бо він це робить за графіком, а не “ліниво”.

Дивлюся на період оновлення… він береться з конфігураційного файлу та локально дорівнює 10000000. З першого погляду взагалі схоже на налаштування “ніколи не оновлювати”. Але то зважаючи на те, в яких це одиницях. По коду виходить, що це значення читається як time.Duration, а Duration вимірюється не в мілі- та не в мікро-, а в наносекундах. Тож цей астрономічний на вигляд період насправді дорівнює лише одній сотій секунди! Виходить, що цей компонент практично не припиняв працювати — звідси й витрати CPU.

От заради таких ситуацій треба бути готовим взяти в руки профайлер.