Аналіз результатів і пошук вузьких місць
Зміст
Прогін закінчився, і у вас є година графіків та файл результатів на мільйон рядків. Далі починається робота, за яку сеньйору й платять: перетворити «стало повільно» на речення «упирається ось цей ресурс, ось доказ, ось що з цим робити». Тест, який знайшов деградацію, але не назвав винного, коштує майже стільки ж, скільки непроведений: команда однаково не знає, що правити.
Головна пастка теми сформульована в каноні SRE прямо: сервіс під перевантаженням має купу вторинних симптомів, які виглядають як першопричина, і це ускладнює налагодження. Тому вся глава — про те, як не назвати причиною перше, що засвітилося червоним. Це саме та частина роботи, за якою мідла відрізняють від сеньйора: цифри знімає інструмент, а висновок робить людина.
Розподіл замість середнього
Перше правило аналізу — не дивитися на середнє. Усереднення часів відповіді виглядає привабливо, але приховує найважливіше: цілком можливо, що більшість запитів швидкі, а довгий хвіст — набагато, набагато повільніший. Канон формулює це настановою: більшість метрик краще уявляти як розподіли, а не як середні. Задокументований приклад: типовий запит обслуговується приблизно за 50 мс, а 5% запитів — у 20 разів повільніші, і моніторинг лише за середньою не показав би жодної зміни поведінки протягом дня.
Мінімальний набір зрізів — медіана плюс високий (percentile). Самі зрізи й те, як вони рахуються, вводить глава про метрики; тут вони — вхідні дані для висновку. Медіана (вона ж 50-й перцентиль) підкреслює типовий випадок, а 99-й чи 99,9-й показує правдоподібне найгірше значення. Що вища дисперсія, то сильніше типовий досвід залежить від хвоста, і ефект посилюється на високому навантаженні через ефекти черг — тобто хвіст росте саме тоді, коли ви тестуєте.
Ще одна властивість даних, яку регулярно ігнорують: розподіл скошений. Жоден запит не відповість швидше ніж за 0 мс, а на 1000 мс означає, що успішних відповідей понад цю межу не буде. Тому не можна припускати, що середнє й медіана близькі, і взагалі не варто припускати нормальний розподіл, доки цього не перевірили. Логарифмічна вісь на графіку затримки — норма, а не хитрість: інакше хвоста не видно.
Найдорожча помилка розділу — арифметика над перцентилями. Готовий квантиль не можна перерахувати під інше вікно, і, найголовніше, квантилі не агрегуються: усереднення готових квантилів із кількох реплік дає статистично беззмістовні значення. Гістограма зберігає розподіл у вигляді, який агрегувати можна, а зведення (summary) рахує квантилі всередині застосунку й віддає готовими — у порівняльній таблиці канону для нього стоїть Not aggregatable. Пастка є й у гістограм: класичні гістограми з різними межами кошиків загалом не агрегуються між собою, а похибка квантиля обмежена шириною кошика. Звести p95 двох сервісів із різними кошиками не можна — і це питання даних, а не запиту. Тому для аналізу потрібна сира вибірка з мітками часу, а не чужа зведена статистика.
Читаючи дашборди, памʼятайте про . Лічильник (counter) обнуляється при перезапуску процесу — раптове падіння його графіка означає рестарт, а не зменшення величини; сирий лічильник читати марно, потрібна частота його зростання, а від датчика (gauge) похідну не беруть ніколи. Ряд, якого ще немає, нулем не є: правило поверх метрики помилок мовчатиме саме тоді, коли має спрацювати. І дрібниця, що найчастіше ламає зіставлення дашборда зі звітом: канон вимагає одну одиницю на імʼя метрики, час — у секундах, частку — у долях одиниці, а не у відсотках.
Орієнтир для співбесіди: канонічна таблиця метрик ISTQB CT-PT (§4.1.2) зупиняється на 90-му перцентилі. p95 і p99 приходять із практики SRE та інструментів — назвати обидва факти сильніше, ніж один.
Кореляція симптомів із ресурсами: USE, RED і спуск до коду
— це процедура, а не набір графіків: від симптому в метриках до названого ресурсу, а від ресурсу — до коду. Перший прохід задає метод USE: для кожного ресурсу перевірити утилізацію (resource utilization), (saturation) й помилки — саму трійку вводить глава про метрики, тут вона працює як перший крок процедури. Канон одразу каже, що робити далі: якщо ви пройшли чекліст, а проблема лишилася, переходьте до заглиблення й аналізу затримки.
Головна деталь чекліста: насичення міряють чергою, а не відсотком. Черга виконання, довша за кількість процесорів, і середня черга до диска понад одиницю — сигнали, яких не видно в «CPU 100%». Сюди ж дві пастки читання: одне гаряче ядро може бути спричинене одним гарячим потоком або привʼязаним апаратним перериванням, і загальна утилізація таке ховає; а відкинуті пакети канон навмисно зараховує і до насичення, і до помилок, бо вони виникають з обох типів подій. Ресурси при цьому ширші, ніж «процесор, памʼять, диск, мережа»: чекліст ділить їх на фізичні (включно з шинами) і програмні — мʼютекси, ліміт задач, дескриптори файлів. Саме програмні й виявляються тим, у що впирається система, коли залізо ще має запас.
Друга половина картини — сервіси. (RED method) задає три метрики на кожен сервіс: частоту запитів, кількість невдалих запитів за секунду й розподіли часу, який займає кожен запит. Автор пише саме «distributions», а не «середнє», і вимагає подавати як частку від частоти запитів — «100 помилок» без знаменника не означає нічого. Межу він формулює сам: USE — рамка для ресурсів, і абстракція натягується, коли говоримо про сервіси; при цьому RED працює лише для сервісів, керованих запитами, і ламається для пакетних або потокових.
Нижній поверх — профілювання. (continuous profiling) описують як сигнал спостережуваності, що показує використання ресурсів аж до номера рядка вихідного коду, і рекомендований порядок збігається з нашим: почати з загальносистемної спостережуваності й спускатися до висновків на рівні коду. Графік flame graph відповідає на питання, якого таблиця метрик не ставить, — хто кого викликав і де згорів час. Серверний профіль і панель продуктивності браузера при цьому різні предмети: висновки одного на інший не переносять.
Нарешті, самі дані. Для аналізу потрібні окремі точки з мітками часу, а не лише агрегована статистика: інструмент віддає обидві форми, і корисною є друга. У полях результату JMeter, крім й коду, є імʼя вузла, що згенерував семпл, — воно й показує, чи всі генератори працювали однаково. Канон ISTQB вимагає того самого з іншого боку: моніторинг налаштовують так, щоб зібрати всі потрібні метрики, а машини генерації моніторять теж. Як це будують — глава про моніторинг, APM і спостережуваність.
Точка насичення: чому «перегин» не є числом
Термін із найслабшим фундаментом у всій темі. У математиці (knee point) — місце, де крива помітно згинається, переходячи від високого нахилу до низького чи навпаки; в оптимізації це точка спадної віддачі, де вигода вже не зростає швидко й більше не варта витрат. У перекладі на криву навантаження: далі за перегином додавання більше не додає пропускної здатності, а лише подовжує час відповіді.
Тепер чесна частина. Канонічного означення цієї точки для кривої навантаження не існує: поняття запозичене з математики, і джерело, яке його означує, — енциклопедичне й математичне, а не стандарт тестування. Насичення як стан ресурсу канон дає (це метод USE), а точка перегину на кривій «навантаження → час відповіді» — ні; це два різні предмети, які часто злипаються в одну назву. Разом із означенням запозичується і його головна вада: графічні уявлення про перегин критикують через залежність від масштабу координат, бо різний вибір масштабу дає різні точки як «перегин», і цій критиці щонайменше з 1940-х.
Наслідок один і жорсткий: «точка насичення на графіку» не є обʼєктивним числом. Розтягнули вісь — перегин переїхав. Тому в звіті точку називають разом із критерієм, за яким її визначили. Джерело розводить два способи: неформальний, коли точку визначають візуально, і формальний, де використовують явну цільову функцію під конкретну задачу оптимізації. Із формальних згадані алгоритм Kneedle, що спирається на поняття кривизни, і простіша альтернатива — знайти точку, де в даних відбувається «зміна фази», підганяючи дві прямі лінійною . Друге прямо застосовне до графіка прогону: одна пряма до перегину, друга після, і точка їх перетину відтворювана незалежно від того, як намальовані осі.
Типові вузькі місця: пули, БД, локи, зовнішні виклики
Найдешевший діагностичний інструмент розділу — чотири з канону. За формою кривої видно, у якому ви випадку, і кожен веде до свого набору гіпотез.
| режим | що видно | типові причини за каноном |
|---|---|---|
| повільно на будь-якому навантаженні | відповідь неприйнятна незалежно від навантаження | погана схема або реалізація БД, мережева затримка, фонові навантаження |
| повільно на середньому й високому | деградація вже в межах нормального очікуваного діапазону | насичення одного або кількох ресурсів, змінні фонові навантаження |
| деградація з часом | відповідь погіршується поступово або різко з часом | витоки памʼяті, фрагментація диска, ріст мережевого навантаження й файлового сховища, неочікуване зростання БД |
| погана обробка помилок під перевантаженням | час відповіді прийнятний, а обробка помилок деградує | недостатні пули ресурсів, замалі черги й стеки, надто швидкі таймаути |
Зверніть увагу, які причини канон називає для останнього рядка: пули. Їх же він називає й окремо — як предмет пошуку в тесті на витривалість. до бази даних, пул потоків і локи — повноцінні ресурси з тими самими трьома метриками, що й процесор: у мʼютекса утилізація — час утримання лока, насичення — потоки в черзі на нього; у пулу потоків утилізація — час, коли потоки були зайняті, насичення — кількість запитів, що чекають на обслуговування. Саме тому «процесор на 40%» нічого не спростовує.
Звідси й список того, що просити в розробки заздалегідь. Для пулу потоків — запити в черзі, зайняті й загальні потоки і, найкорисніше, скільки речі чекали в черзі. Для кешу — запити, влучання, затримка плюс метрики системи, перед якою кеш стоїть. Для пулу зʼєднань до БД — щонайменше запити, помилки й затримка з розрізненням баз; те саме стосується будь-якої бібліотеки, що ходить по зовнішній ресурс, а отже й зовнішніх API. І настанова, яка економить години суперечок: онлайн-сервіси моніторять і з боку клієнта, і з боку сервера, бо різна поведінка двох сторін — цінна інформація для налагодження, а не помилка виміру. Саме тому розбіжність між часом у звіті тесту й часом на дашборді законна.
Далі — механіка, через яку симптом майже ніколи не стоїть там, де причина. Нестача процесора сама породжує конкурентність: запити обробляються довше, тож більше їх обробляється одночасно, і це зачіпає памʼять, активні потоки, дескриптори файлів і ресурси бекендів. Черги насичуються, зриваються дедлайни RPC (робота витрачена намарно, а клієнти повторюють запити), падає частка влучань у кеш — і це дає більше викликів до бекендів, які можуть перевантажитися вже самі.
Ця спіраль названа в каноні поіменно й ілюструє головне: те, що видно в моніторингу, зазвичай не причина. Канонічний приклад веде девʼятьма ланками від погано налаштованих параметрів збирача сміття на фронтенді до провалу базових перевірок працездатності, і висновок джерела варто памʼятати: у настільки складних ситуаціях малоймовірно, що причинний ланцюг буде повністю діагностовано під час аварії, — надто якщо фронтенд і бекенд мають різних власників. Робочий висновок: під час прогону фіксують факти з мітками часу, а причинний ланцюг збирають після.
І теза, без якої звіт розсипається на першому питанні бізнесу: ємність міряють у ресурсах, а не в запитах за секунду — різні запити мають кардинально різні вимоги до ресурсів, а вартість запиту змінюється навіть від часу доби.
Межа глави: , індекси, локи і EXPLAIN живуть у розділі про бази даних — індекси і швидкодія запитів та рівні ізоляції й блокування. Тут БД цікавить як названа каноном причина режиму і як ресурс з утилізацією й насиченням.
Деградація з часом: що видно лише в довгому прогоні
Третій режим відмови не знаходиться ні коротким прогоном, ні високим навантаженням: він проявляється на звичайному навантаженні, витриманому довго. Вид тесту під нього — (endurance, в інструментах — soak): стабільність системи протягом проміжку часу, специфічного для її операційного контексту, під значним навантаженням протягом значного часу. Канон називає предмет пошуку поіменно — тип тестування перевіряє, що немає проблем ємності ресурсів, і дає три приклади: витоки памʼяті, зʼєднання з базою даних, пули потоків, які згодом деградують продуктивність і/або спричиняють відмови в точках зламу. Інструментальна шпаргалка це підтверджує: навантаження середнє, тривалість — довга, години.
Аналізують такий прогін інакше. Метрика тут — нахил, а не значення. Одне зведене число за весь прогін приховає рівно те, що ви шукали: якщо перша година дала p95 у 300 мс, а остання — 900 мс, у звіт піде одна проміжна цифра, у якій зростання не видно, — а «усереднити» два готові p95 і назвати це числом прогону не можна тим більше. Дивляться на тренд часу відповіді й памʼяті всередині прогону при незмінному навантаженні, а сам витік видно з боку системи, а не з боку генератора — отже, окремий моніторинг процесу тут обовʼязковий.
Дзеркальна пастка усереднення живе й на боці інфраструктури: сплеск високої утилізації може спричинити насичення, хоча усереднена по довгому інтервалу утилізація низька. Задокументований випадок — інструмент звітував пʼятихвилинними середніми не вище 80%, а всередині них утилізація на секунди сягала 100%. Звідси обовʼязкове питання до будь-якого графіка утилізації: яке в нього вікно усереднення?
Пастки оптимізації
Наша практика (не канон). Далі — те, як це роблять команди, з якими ми працювали; окремого стандарту чи специфікації під це немає.
Найчастіша пастка — оптимізують те, що видно, а не те, що болить. У звіті першою в очі впадає найповільніша , хоча її частка в реальному профілі може бути мізерною: виграш удвічі на 2% трафіку не змінить нічого, а тиждень роботи зʼїсть. Друга — зміна, яка не знімає вузьке місце, а переносить його: збільшили пул зʼєднань, і тепер упирається сама база; підняли кількість , і закінчилися дескриптори. Це нормальний хід подій; ненормально — оголошувати такий крок перемогою, не повторивши вимір.
Третя — «оптимізації», що маскують симптом: ширші таймаути, доданий , збільшена черга. Час відповіді в звіті поліпшується, бо повільні запити перестали падати, а не почали бути швидшими. Четверта — кеш як універсальна відповідь: він справді допомагає, але змінює , і прогін на прогрітому кеші з прогоном на холодному вже не порівняти. Пʼята — тюнинг під стенд: параметри, підібрані на середовищі з іншим обсягом даних, на проді поводяться інакше.
Робоче правило одне: зміна вважається доведеною лише повторним прогоном на тому самому профілі, тих самих даних і тому самому середовищі. Усе інше — гіпотеза, навіть якщо графік став гарнішим.
Формат висновку: симптом → гіпотеза → підтвердження → рекомендація
Одразу межа: сам — робоча рамка, якою зручно тримати структуру, а не стандарт, на який можна послатися. Джерела є під окремими вимогами всередині кроків, а не під самою послідовністю.
Симптом формулюють у цифрах розподілу й у часі: «з 14:20, на рампі понад 400 віртуальних користувачів, p95 транзакції оформлення зріс із 400 мс до 2,4 с, частка помилок — з 0,1% до 3%». Прикметники на кшталт «підгальмовує» з цього рядка прибирають.
Гіпотеза називає ресурс або сервіс, а не абстракцію: «упирається пул зʼєднань до БД — черга очікування зʼєднання росте раніше, ніж час самих запитів».
Підтвердження — місце, де провалюється більшість звітів, бо перед звинуваченням системи треба зняти три альтернативні пояснення.
- Генератор. Якщо інструмент використовує 100% процесора, тест зазнає , і метрики покажуть значно більший час відповіді, ніж є насправді. Пороги канон дає числами: щонайменше 20% простою процесора, памʼять не вище 90%, мережа не в поличці, своп вимкнено. Помилки розводять за :
socket: too many open files— це ми вперлися в дескриптори,read: connection reset by peer— система скинула зʼєднання,context deadline exceeded— запит пішов, а відповідь не встигла. І класика: більшість елементівListenerу JMeter тримає копію кожного семпла, який показує, тож «дерево результатів» під навантаженням міряє власний інтерфейс. - Середовище. Генератор не ставлять на сервер застосунку: це додає йому обчислювального навантаження, і результати будуть зіпсовані. А тест «на кластер» без окремого налаштування бʼє в одну машину, бо за замовчуванням використовується кеш DNS віртуальної машини Java. Деталі стенда — глава про середовище й генератори.
- Валідація. Прогін без перевірки вмісту відповіді міряє швидкість сторінки помилки. Усталена перевірка стверджує лише факт наявності значення, а не його правильність, і перевірки пишуть стійкими до відсутнього тіла: канонічний приклад — перевірка довжини тіла працює, поки система здорова, і ламається саме тоді, коли та почала падати, даючи помилку в скрипті замість зафіксованої відмови системи. Як це роблять у сценарії — глава про кореляцію, параметризацію й тестові дані.
Рекомендація складається з дії, очікуваного ефекту й способу перевірки. І обовʼязково — межа перенесення на продакшн: канон вимагає або тримати середовище максимально близьким до прода, або чітко розуміти відмінності й те, як результати проєктуватимуться. «Продуктивність є нелінійною функцією середовища» тут не риторика: міркування «стенд удвічі слабший, отже прод витримає вдвічі більше» — не консервативна оцінка, а помилка. Питання «чи стало гірше, ніж було» — окрема задача порівняння прогонів, і живе вона в главі про перф-тести в CI/CD.
Типові помилки
- «Знизили навантаження назад — не піднялося, отже справа не в навантаженні» → виглядає як спростування гіпотези, а насправді каскад не зупиняється поверненням: якщо сервіс був здоровим на 10 000 запитів за секунду й почав падати на 11 000, зниження до 9000 майже напевно не допоможе, а в прикладі, де здоровими лишилося 10% серверів, частоту довелося б знизити приблизно до 1000.
- «Під час аварії стрибнув трафік — ось причина» → виглядає як очевидний винуватець, а насправді сплеск може бути наслідком: без бюджету ретраїв кількість запитів зростає майже втричі, а бюджет на клієнта (частка ретраїв нижче 10%) утримує зростання коло 1,1x.
- «Бекенд просто відхиляє запити, отже він не перевантажений» → виглядає як економія ресурсів, а насправді сервіс може перевантажитися, навіть якщо більшість його процесора витрачається саме на відхилення запитів.
- «Ми тримаємо 5000 RPS» → виглядає як характеристика ємності, а насправді без складу запитів цифра не переноситься на інший мікс: різні запити мають кардинально різні вимоги до ресурсів, і співвідношення змінюється саме собою.
- «Ось точка насичення, її видно на графіку» → виглядає як вимір, а насправді положення перегину залежить від масштабу осей: розтягнули вісь — точка переїхала. Називають критерій, а не показують пальцем.
- «Load average 20 — система перевантажена» → виглядає як пряма метрика процесора, а насправді в Linux ця величина рахує й задачі в неперервному стані очікування (зазвичай вводу-виводу), тож без перевірки, що саме там чекає, висновок некоректний.
- «Злили результати двох прогонів в один графік» → виглядає як зручність, а насправді інструмент не очищає поточні дані перед завантаженням нового файлу: для розподіленого прогону це фіча, для порівняння двох прогонів — зіпсовані дані.
Підсумок
- Рішення ухвалюють за розподілом, а не за середнім. Мінімум — медіана й високий перцентиль; готові квантилі не усереднюються ніколи, а гістограми з різними кошиками не зводяться між собою.
- Порядок аналізу фіксований: симптом → ресурс → код. USE закриває ресурси, RED — сервіси, профіль — останній поверх. Насичення шукають у чергах, а не у відсотках зайнятості.
- Те, що видно в моніторингу, зазвичай не причина. Вторинні симптоми виглядають переконливіше за першопричину, тож під час прогону фіксують факти з мітками часу, а ланцюг збирають після.
- Точка насичення не є обʼєктивним числом. Її називають разом із критерієм; канонічного означення для кривої навантаження немає, і на співбесіді це варто сказати вголос.
- Висновок без знятих альтернатив — не висновок. Поки не показано, що не вперся генератор, що середовище не спотворює вимір і що відповіді валідувалися, цифри описують тест, а не систему.
Можливі питання
- «Медіана не змінилася, а
p99виріс утричі. Що це означає?» — перевіряють розуміння форми розподілу: типовий користувач нічого не помітив, а хвіст став гіршим і на високому навантаженні росте через ефекти черг. Сильна відповідь одразу пропонує наступний крок — насичення ресурсів у тому самому вікні часу. - «Як ви шукаєте вузьке місце?» — очікують метод, а не перелік інструментів: три метрики на кожен ресурс, потім три метрики на кожен сервіс, потім профіль; і окремо — що будь-який ненульовий рівень насичення вже привід розслідувати, а не лише черга, яка виросла вдесятеро.
- «Утилізація всіх ресурсів нижче 60%, а час відповіді росте. Ваші гіпотези?» — дивляться, чи згадаєте ви вікно усереднення, програмні ресурси (пул зʼєднань, пул потоків, локи) і зовнішній виклик як кандидата.
- «Що таке точка насичення і як ви її визначаєте?» — слабка відповідь показує пальцем на графік. Сильна називає критерій і чесно каже, що положення перегину залежить від масштабу осей.
- «Під стрес-тестом система впала, ми знизили навантаження — вона не піднялася. Це баг?» — питання про й відновлення. Очікують згадку, що повернення до попереднього рівня відновлення не гарантує, і що вихід із деградованого режиму перевіряють окремо.
- «Як ви доведете, що впирається система, а не ваш генератор?» — питання рівня сеньйора: ресурси генератора в звіті, розведення помилок за походженням, моніторинг самих машин навантаження.
- «Покажіть, як виглядає ваш звіт після прогону» — дивляться на структуру мислення: симптом у цифрах, названа гіпотеза, докази зі знятими альтернативами, рекомендація з межею перенесення на продакшн.
Джерела
- Google SRE Book — Chapter 4. Service Level Objectives — середнє приховує хвіст, «розподіли, а не середні», приклад із 5% запитів, скошеність даних, логарифмічна вісь.
- Prometheus — Histograms and summaries — заборона агрегувати й перераховувати готові квантилі,
Not aggregatableдля зведення, похибка в межах кошика. - Prometheus — Metric types — чотири типи метрик, обнуління лічильника при перезапуску, неагрегованість гістограм із різними межами кошиків.
- Prometheus — Instrumentation — вибір лічильника чи датчика, частота замість сирого лічильника, відсутній ряд як мовчазне правило.
- Prometheus — Metric and label naming — одна одиниця на імʼя метрики, час у секундах, частка в долях одиниці.
- Apache JMeter — User's Manual: Glossary — медіана як 50-й перцентиль і
90% Lineяк значення, нижче за яке лежать 90% семплів. - ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — §4.1.2: таблиця метрик, найвищий зріз у якій — 90-й перцентиль.
- Grafana k6 — Thresholds — перцентиль як умова pass/fail і синтаксис
p(N). - Grafana k6 — Metrics — той самий поділ на чотири типи метрик в інструменті навантаження.
Кореляція симптомів із ресурсами: USE, RED і спуск до коду
- Brendan Gregg — USE Method: Linux Performance Checklist — чекліст як перший прохід, черги як сигнали насичення, гаряче ядро, фізичні й програмні ресурси, відкинуті пакети як подвійний індикатор.
- Brendan Gregg — The USE Method — правило «утилізація, насичення, помилки для кожного ресурсу» та одиниці виміру.
- Tom Wilkie — The RED Method: key metrics for microservices architecture (2017) — три метрики на сервіс,
Durationсаме як розподіли, частка помилок від частоти запитів, межа з USE і непридатність для пакетних сервісів. - Prometheus — Instrumentation — той самий поділ незалежно: запити, помилки й затримка для сервісів, що обслуговують запити.
- Grafana Pyroscope — documentation home — профілювання до рядка коду, порядок «від системної спостережуваності вниз»,
flame graph. - Chrome DevTools — Analyze runtime performance — браузерний профайлер як окремий предмет із власними джерелами шуму.
- Apache JMeter — Introduction to listeners — склад полів результату, зокрема імʼя вузла, що згенерував семпл.
- Grafana k6 — Results output — дві форми виводу: агрегована статистика або окремі точки даних.
- Google SRE Book — Chapter 22: Addressing Cascading Failures — вторинні симптоми, що виглядають як першопричина, і межа діагнозу під час аварії.
- ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — вимога налаштувати моніторинг під потрібні метрики й моніторити машини генерації.
Точка насичення: чому «перегин» не є числом
- Wikipedia — Knee of a curve — математичне означення перегину й оптимізаційне прочитання через спадну віддачу, залежність положення точки від масштабу осей і критика цього щонайменше з 1940-х, візуальний проти формального способу, алгоритм Kneedle і підгонка двох прямих. Джерело енциклопедичне (
secondary) і не стосується тестування: канонічного означення для кривої навантаження в реєстрі немає, тож твердження цього підрозділу підтвердженими каноном не вважаються.
Типові вузькі місця: пули, БД, локи, зовнішні виклики
- ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — §1.5: чотири режими відмови продуктивності з причинами, серед них недостатні пули ресурсів, замалі черги й стеки та надто швидкі таймаути.
- Brendan Gregg — The USE Method — мʼютекси, пули потоків і дескриптори файлів як ресурси з утилізацією й насиченням.
- Google SRE Book — Chapter 22: Addressing Cascading Failures — ланцюжок вичерпання ресурсів, спіраль збирача сміття, падіння частки влучань у кеш, девʼятиланковий приклад.
- Google SRE Book — Chapter 21: Handling Overload — ємність у ресурсах замість запитів за секунду й різна вартість запитів.
- Prometheus — Instrumentation — метрики пулу потоків, кешу й пулу зʼєднань до БД; вимірювання з боку клієнта і сервера.
- Prometheus — Overview — джерело теми «інструментування сервісу», з якої підрозділ бере перелік метрик пулів і кешу; сам збір метрик і його модель розбирає глава про моніторинг.
- Grafana — documentation home (OSS and Enterprise) — те саме джерело теми інструментування; шар візуалізації як предмет — у главі про моніторинг, тут він не розбирається.
- Grafana Pyroscope — documentation home — те саме джерело теми інструментування; профілювання як окремий крок — у главі про моніторинг.
- Wikipedia — Application performance management — те саме джерело теми інструментування; APM як поняття (і чому в нього немає канону) — у главі про моніторинг. Джерело
secondary.
Деградація з часом: що видно лише в довгому прогоні
- ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — означення тестування на витривалість через операційний контекст, перелік того, що воно шукає, і причини режиму «деградація з часом».
- ISTQB Glossary — означення
endurance testingчерез значне навантаження протягом значного проміжку часу. - Grafana k6 — Load test types — параметри виду: середнє навантаження, тривалість у годинах, пріоритет за профілем системи.
- Brendan Gregg — The USE Method — сплески всередині вікна усереднення й випадок із пʼятихвилинними середніми до 80% при секундних 100%.
Формат висновку: симптом → гіпотеза → підтвердження → рекомендація
- Grafana k6 — Running large tests — тротлінг генератора й завищені цифри часу відповіді, пороги процесора, памʼяті, мережі й свопу, розведення помилок за походженням, вимога не припускати нічого про відповідь.
- Apache JMeter — Best Practices — режим командного рядка, мінімум елементів
Listenerі , заборона «дерева результатів» під навантаженням, згадка проблемиCoordinated Omission. - Apache JMeter — Introduction to listeners — більшість елементів
Listenerтримає копію кожного семпла, який показує. - Apache JMeter — Remote (Distributed) Testing — запуск на сервері застосунку псує результати; генератори тримають у тому самому сегменті мережі.
- Apache JMeter — User's Manual: Component Reference — кеш DNS віртуальної машини Java, через який навантаження отримує лише один сервер кластера; асерт тривалості.
- Gatling — Checks — неявний
existsяк усталена перевірка й висока кардинальність метрик помилок при логуванні фактичних значень. - Brendan Gregg — USE Method: Linux Performance Checklist — дескриптори файлів і ліміт задач як ресурси генератора з власними помилками.
- Google SRE Book — Chapter 17: Testing for Reliability — прод не є герметичним і не представляє жодної окремої версії бінарника, тож «prod-like» — керована відмінність.
- Google SRE Book — Chapter 22: Addressing Cascading Failures — вимога перевіряти правильність під високим навантаженням для сервісів зі станом і кешем.
- ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — §4.2.8: продуктивність як нелінійна функція середовища й вимога описати проєкцію результатів на продакшн.
Чому в звіті про навантажувальний прогін не можна обмежитися середнім часом відповіді?
Середнє — єдине число, яке навмисно знищує форму даних. Якщо девʼять запитів із десяти обслужено за 50 мс, а десятий — за секунду, середнє дасть 145 мс: значення, якого не побачив жоден користувач. Канон SRE радить уявляти метрики як розподіли й підкріплює це задокументованим випадком, де типовий запит займав близько 50 мс, а 5% запитів були у двадцять разів повільнішими, — графік середньої за добу при цьому не поворухнувся. Практичний наслідок простий: середнє не червоніє тоді, коли деградація вже сталася, тож рішення «випускаємо чи ні» на ньому не будують. У звіт іде щонайменше медіана й високий (percentile), а середнє лишається довідковою цифрою.
Який мінімальний набір зрізів ви показуєте і на яке питання відповідає кожен?
Мінімум — медіана (вона ж 50-й перцентиль) плюс високий зріз, p99 або p99.9. Медіана відповідає на питання «як почувається типовий користувач», високий перцентиль — «наскільки поганим буває правдоподібно найгірший випадок». Чим більший розкид, тим сильніше загальне враження від системи визначає саме хвіст, а на високому навантаженні хвіст росте додатково через ефекти черг — тобто саме тоді, коли ви й міряєте. Корисно памʼятати зрізів: таблиця метрик у силабусі ISTQB CT-PT доходить лише до 90-го перцентиля, а p95 і p99 прийшли з практики SRE й інструментів навантаження. На співбесіді назвати обидва джерела сильніше, ніж просто сказати «дивлюся p95». І окремо варто згадати форму даних: розподіл часів відповіді скошений — швидше за 0 мс не відповість ніхто, а обрізає вибірку згори, — тому припущення «середнє й медіана десь поруч» не працює, а логарифмічна вісь на графіку затримки є нормою, бо в лінійному масштабі хвоста просто не видно.
На дашборді графік метрики різко впав до нуля. Що це означає?
Насамперед подивіться на . Лічильник (counter) обнуляється при перезапуску процесу, тож обрив його графіка вниз — це майже завжди рестарт, а не зменшення реальної величини. Сирий лічильник взагалі читати марно: змістовною є частота його зростання, а не накопичене число. Дзеркальна помилка — брати похідну від датчика (gauge): датчик показує миттєве значення, яке вільно ходить угору й униз, і швидкість його зміни нічого не означає. Ще одна пастка того самого класу: ряду, якого ще немає, не існує, а не «дорівнює нулю», — правило поверх метрики помилок мовчатиме саме в той момент, коли воно мало б спрацювати. І дрібниця, яка ламає зіставлення дашборда зі звітом найчастіше: канон вимагає однієї одиниці на імʼя метрики, час — у секундах, частку — у долях одиниці, тож 0.03 на дашборді й «3%» у звіті можуть бути тим самим числом.
Три репліки сервісу віддали свої p95. Чи можна взяти середнє й отримати спільний p95?
Ні, і це найдорожча арифметична помилка теми. Готовий квантиль не перераховується під інше вікно часу й не агрегується між джерелами: середнє з готових квантилів дає статистично беззмістовне число, яке виглядає цілком пристойно. Різниця між типами метрик тут вирішальна: зведення (summary) рахує квантилі всередині застосунку й віддає готовими — у порівняльній таблиці Prometheus навпроти нього стоїть Not aggregatable, тоді як гістограма зберігає розподіл у формі, яку зводити можна. Але й гістограми не рятують автоматично: ряди, у яких межі не збігаються, звести між собою здебільшого не вдасться, а точність порахованого квантиля не буває кращою за ширину бакета. Тому звести p95 двох сервісів із різними бакетами не вийде — і це обмеження даних, а не невдалого запиту. Робочий висновок: для аналізу тримайте сиру вибірку з мітками часу, а не чужу зведену статистику.
Як ви шукаєте вузьке місце? Опишіть метод, а не інструменти.
Це процедура з трьох поверхів, і саме її від кандидата й чекають. Перший прохід — метод USE по ресурсах: для кожного ресурсу дивимось утилізацію (resource utilization), (saturation) і помилки; чекліст дешевий і закриває більшість випадків. Якщо ресурс не назвався, другий поверх — по сервісах: частота запитів, невдалі запити за секунду й розподіли тривалості кожного запиту. Коли ресурс або сервіс названо, спускаємось на третій поверх — профілювання, яке показує споживання аж до номера рядка вихідного коду; flame graph відповідає на питання, якого таблиця метрик не ставить: хто кого викликав і де саме згорів час. Порядок не декоративний — документація профілювання радить те саме: спершу дивитися на систему цілком, а вже потім опускатися до висновків про конкретний код. І варто сказати вголос, що розслідування починають не з черги, яка виросла вдесятеро, а з будь-якого насичення, відмінного від нуля.
Як ви міряєте насичення ресурсу? Чому не відсотком утилізації?
Насичення — це про те, скільки роботи чекає, а не скільки виконується, тому міряють його чергою. Довжина черги виконання понад кількість ядер і середня черга до диска більша за одиницю — саме ті сигнали, яких показник зайнятості процесора не дає взагалі: сто відсотків утилізації однаково виглядають і коли черги немає, і коли в ній сотня задач. Два додаткові підступи читання. Перший: одне гаряче ядро може бути наслідком одного гарячого потоку або привʼязаного апаратного переривання, і загальна утилізація по машині таке рівно розмазує. Другий: відкинуті пакети канон свідомо зараховує і до насичення, і до помилок, бо вони породжуються обома типами подій, — тож не дивуйтеся, побачивши їх у двох рядках чекліста. І головне: перелік ресурсів ширший за «процесор, памʼять, диск, мережа» — до нього входять фізичні шини та програмні ресурси: мʼютекси, ліміт задач, дескриптори файлів.
Чим RED відрізняється від USE і де RED не працює?
USE — рамка для ресурсів, RED — для сервісів, і автор RED сам пояснює, звідки взявся другий метод: абстракція USE натягується, коли ресурси доводиться підмінювати сервісами. RED дає три метрики на кожен сервіс: частоту запитів, кількість невдалих запитів за секунду й розподіли часу обробки — саме розподіли, а не середнє, це в оригіналі підкреслено. він вимагає подавати відносно частоти запитів: «сто помилок» без знаменника не означає нічого, бо сто зі ста тисяч і сто зі ста — різні світи. Межа методу теж названа автором: RED придатний лише для сервісів, керованих запитами, і ламається на пакетній чи потоковій обробці, де поняття «запит» відсутнє. Знати цю межу корисніше, ніж знати сам акронім: питання «а як ви застосуєте RED до нічного ETL» — типова перевірка на завчену відповідь.
Залізо завантажене менш ніж на 60%, а час відповіді росте. Які у вас гіпотези?
Перше питання не до системи, а до графіка: яке в нього вікно усереднення. Сплеск високої утилізації цілком спричиняє насичення, лишаючись невидимим у довгому середньому, — задокументований випадок, коли інструмент звітував пʼятихвилинні середні не вище 80%, а всередині них утилізація на секунди сягала ста. Друга група гіпотез — програмні ресурси, яких на дашборді заліза немає взагалі: до бази, пул потоків, локи. Вони теж мають утилізацію й насичення: для мʼютекса утилізацією буде час, протягом якого лок хтось тримав, а насиченням — потоки, що вишикувалися по нього; для пулу потоків утилізацією буде частка часу, коли потоки чимось займалися, а насиченням — скільки запитів стоїть і чекає своєї черги. Третій кандидат — зовнішній виклик: власне залізо простоює, доки ваш сервіс чекає на чужий. Саме тому «процесор на сорока відсотках» нічого не спростовує, і саме тому чекліст USE ділить ресурси на фізичні й програмні.
Які метрики ви просите в розробки заздалегідь, ще до прогону?
Ті, без яких аналіз перетвориться на здогади про програмні ресурси. Для пулу потоків просять чотири речі: скільки запитів у черзі, скільки потоків зайнято, скільки їх усього і — найцінніше — як довго робота чекала на свою чергу; саме воно й відрізняє «пул вичерпано» від «самі операції стали повільними», бо це насичення ресурсу, а не його утилізація. Для кешу — запити, влучання й затримка, обовʼязково разом із метриками системи, перед якою кеш стоїть: інакше падіння частки влучань виглядає нешкідливо, хоча означає кратний ріст навантаження на бекенд. Для пулу зʼєднань — як мінімум кількість запитів, помилки й затримка, розбиті по базах; той самий набір потрібен від кожної бібліотеки, яка лізе за межі процесу, — зокрема від клієнтів чужих API. І окрема домовленість, яка економить години суперечок: онлайн-сервіси міряють і з боку клієнта, і з боку сервера, бо розбіжність між двома сторонами — цінна інформація для налагодження, а не дефект виміру. Саме тому час у звіті генератора законно відрізняється від часу на дашборді сервісу.
Що таке точка насичення і як ви її визначаєте?
Слабка відповідь показує пальцем на графік, тож почніть із чесної рамки. (knee point) — математичне поняття: місце, де крива переходить від великого нахилу до малого; в оптимізації це точка спадної віддачі, після якої вигода вже не варта витрат. На кривій навантаження це читається так: за перегином додавання більше не збільшує , а лише розтягує . Далі важливе: канонічного означення саме для кривої навантаження немає — поняття запозичене з математики, а разом із ним запозичена і його головна вада: положення перегину залежить від масштабу осей, і цій критиці щонайменше з сорокових років. Отже, точка насичення не є обʼєктивним числом: розтягнули вісь — вона переїхала. Тому в звіті її називають разом із критерієм: або чесно кажуть «визначено візуально», або застосовують формальний спосіб — алгоритм Kneedle через кривизну чи простішу підгонку двох прямих лінійною , де перетин прямих до й після зламу відтворюється незалежно від того, як намальовані осі.
Як ви за формою результату звужуєте коло гіпотез?
Найдешевший діагностичний інструмент — чотири з силабусу ISTQB, бо кожен веде до власного набору причин. Якщо система повільна на будь-якому навантаженні, включно з мінімальним, — дивимось у бік схеми чи реалізації бази, мережевої затримки й фонових навантажень; навантаження тут ні до чого. Якщо деградація починається на середньому й високому рівнях, тобто вже всередині очікуваного діапазону, — підозрюємо насичення одного чи кількох ресурсів і змінні фонові процеси. Якщо відповідь погіршується з часом при незмінному навантаженні — це витоки памʼяті, фрагментація диска, ріст файлового сховища чи неочікуване зростання бази. А якщо час відповіді прийнятний, але під перевантаженням розсипається обробка помилок, канон називає причини поіменно: недостатні пули ресурсів, замалі черги й стеки, надто швидкі таймаути. Ця класифікація коштує пʼять хвилин, а економить години сліпого блукання дашбордами.
Чим аналіз довгого прогону відрізняється від аналізу звичайного навантажувального?
Тим, що метрикою стає нахил, а не значення. (endurance, у інструментах — soak) шукає проблеми ємності ресурсів на звичайному навантаженні, витриманому довго: витоки памʼяті, зʼєднання з базою даних, пули потоків, які згодом псують продуктивність або призводять до у точках зламу. Якщо ви подивитесь на зведене число за прогін, ви приховаєте рівно те, що шукали: перша година з p95 у 300 мс і остання з 900 мс зіллються в одну проміжну цифру, у якій зростання не видно, — а «усереднити» два готові p95 і отримати з них число прогону не можна взагалі. Тому дивляться погодинний зріз при незмінному навантаженні й питають не «яке значення», а «чи вийшла крива на плато». І ще одна деталь: сам витік видно з боку системи, а не з боку генератора — генератор бачить лише наслідок, тож окремий моніторинг процесу для такого прогону обовʼязковий.
Чому найпомітніший симптом у моніторингу рідко виявляється першопричиною?
Бо перевантажений сервіс продукує купу вторинних симптомів, які виглядають переконливіше за першопричину, — це прямо сказано в розділі SRE про . Механіка добре відома: нестача процесора сама породжує конкурентність, бо запити обробляються довше й тому їх одночасно більше, а це вже тягне памʼять, активні потоки, дескриптори файлів і ресурси бекендів. Далі насичуються черги, зриваються дедлайни RPC — робота витрачена намарно, а клієнти повторюють запити, — падає частка влучань у кеш, і бекенди отримують кратно більше викликів. Окремо названа спіраль збирача сміття: менше доступного процесора дає повільніші запити, повільніші запити тримають більше памʼяті, більше памʼяті дає більше збирань сміття, які знову зʼїдають процесор. У каноні наведено приклад із девʼяти ланок — від невдалих налаштувань збирача сміття на фронтенді до того, що сервіс перестає проходити елементарні перевірки живучості, — і висновок джерела варто цитувати на співбесіді: у настільки складних ситуаціях причинний ланцюг навряд чи буде повністю розібраний під час самої аварії. Звідси робоче правило: у момент прогону збирають датовані факти, а причинно-наслідковий ланцюг вибудовують уже потім, на холодну голову.
На машині load average дорівнює 20. Система перевантажена?
З цієї цифри такого висновку зробити не можна. У Linux середнє навантаження рахує не лише задачі, що чекають на процесор, а й задачі в неперервному стані очікування — зазвичай це введення-виведення. Тобто двадцять може означати і справжню чергу до процесорів, і двадцять процесів, які мирно чекають на повільний диск чи мережеве сховище, доки процесор простоює. Саме тому в чеклісті USE середнє навантаження свідомо не входить до метрик процесора: воно змішує два різні стани в одне число. Правильний наступний крок — подивитися, що саме там чекає: чергу виконання окремо, а очікування введення-виведення окремо.
Як ви доведете, що впирається система, а не ваш генератор?
Цифрами про сам генератор, і бажано в тому самому звіті. Якщо інструмент навантаження зайняв сто відсотків процесора, він потрапляє в і починає показувати помітно більший час відповіді, ніж є насправді, — деградацію ви побачите, тільки чужу. Пороги канон дає числами: щонайменше пʼята частина процесора вільна, памʼять не вище девʼяноста відсотків, мережа не в поличці, своп вимкнений. Помилки розводять за походженням, і це швидкий спосіб зрозуміти власника проблеми: socket: too many open files означає, що вперлися ми в дескриптори; read: connection reset by peer — що зʼєднання скинула система під тестом; context deadline exceeded — що запит пішов, а відповідь не встигла. Окремо перевіряють конфігурацію самого інструмента: переважна частина елементів Listener у JMeter зберігає в памʼяті кожен показаний семпл, тому увімкнене під навантаженням «дерево результатів» міряє власний інтерфейс, а не застосунок. І нарешті вимога ISTQB, яка закриває тему формально: машини генерації моніторять так само, як систему під тестом.
Прогін дав чудові цифри. Які ще альтернативи треба зняти, перш ніж їм вірити?
Дві, і обидві поза генератором. Перша — середовище: генератор не ставлять на сервер застосунку, бо він додає тій самій машині обчислювального навантаження й псує результати; а тест «на кластер» без окремого налаштування може весь час бити в одну машину через кеш DNS віртуальної машини Java. Друга — валідація: прогін без перевірки вмісту відповіді чудово міряє швидкість сторінки помилки, і чим гірше системі, тим кращі цифри він показує. Тут є ще й пастка другого порядку: усталена перевірка стверджує лише факт наявності значення, а не його правильність, а перевірки на кшталт довжини тіла працюють, поки система здорова, і ламаються саме тоді, коли вона почала падати, — у звіті зʼявиться помилка скрипта замість зафіксованої відмови системи. Тому перевірки пишуть стійкими до відсутнього тіла. Поки ці дві альтернативи не зняті, ваші цифри описують тест, а не систему.
Під час стрес-тесту сервіс ліг; навантаження зменшили назад, а він не ожив. Це баг?
Не обовʼязково: каскад не зупиняється поверненням навантаження назад, і це задокументована властивість, а не дивина. Канонічний приклад прямий: якщо сервіс був здоровим на десяти тисячах запитів за секунду й почав валитися на одинадцяти, зниження до девʼяти тисяч майже напевно не допоможе — частину ємності вже зʼїли перезапуски, холодні кеші й . У прикладі, де здоровими лишилося близько десятої частини серверів, частоту довелося б знизити приблизно до тисячі, тобто на порядок. Тому висновок «раз не піднялася, справа не в навантаженні» — типова . Правильна поведінка — фіксувати це як окрему властивість системи й перевіряти вихід із деградованого режиму окремим сценарієм: скільки треба зняти навантаження й скільки часу потрібно, щоб сервіс повернувся.
Під час аварії стрибнула кількість запитів. Це причина чи наслідок?
Найчастіше наслідок, і зробити такий висновок вголос — ознака зрілості. Коли сервіс починає повільно відповідати або відмовляти, клієнти повторюють запити, і без бюджету спроб кількість запитів зростає майже втричі відносно початкової. Бюджет ретраїв на клієнта, який тримає частку повторів нижче десяти відсотків, обмежує ріст приблизно до 1,1x — різниця між керованою й некерованою ситуацією саме тут. Друга частина відповіді стосується вигляду «економії»: сервіс може бути перевантаженим, навіть якщо більшість його процесора йде на відхилення запитів, тож «він же просто відхиляє, значить не перевантажений» — не аргумент. Практичний висновок для аналізу прогону: сплеск на графіку треба зіставити з мітками часу перших помилок, і якщо помилки почалися раніше за сплеск, ви дивитесь на наслідок.
«Ми тримаємо 5000 RPS». Що не так із цією фразою?
Вона описує один конкретний мікс запитів, а не ємність системи. Різні запити мають кардинально різні вимоги до ресурсів, і навіть співвідношення між ними змінюється саме собою — вартість типового запиту не однакова о третій ночі та о шостій вечора. Тому канон вимагає міряти ємність у ресурсах, а не в запитах за секунду: «стільки-то ядер і стільки-то памʼяті на таку-то одиницю роботи» переноситься на інший профіль, а гола цифра RPS — ні. Практично це означає, що число з попереднього кварталу не є характеристикою системи, якщо профіль трафіку відтоді змінився. У звіті ємність подають разом зі складом навантаження, на якому її отримали, — інакше перше ж питання бізнесу «а якщо частка чекаутів зросте вдвічі» розсипає весь документ.
Покажіть, як виглядає ваш висновок після прогону.
Чотири кроки, і кожен має власний критерій якості. Симптом описують числами розподілу з привʼязкою до часу: не «оформлення підгальмовує», а «починаючи з 25-ї хвилини прогону, коли рампа перевалила за 400 віртуальних користувачів, p95 оформлення пішов із 310 мс на 2,4 с, а частка помилок — з 0,1% до 3,1%». Гіпотеза мусить вказати конкретний ресурс чи сервіс замість загальних слів: «вузьке місце — пул зʼєднань до бази: очікування вільного зʼєднання починає рости ще до того, як подовшали самі запити». Підтвердження — місце, де провалюється більшість звітів: перш ніж звинувачувати систему, треба показати, що не вперся генератор, що середовище не спотворює вимір і що відповіді валідувалися. Рекомендація складається з дії, очікуваного ефекту й способу перевірки, і обовʼязково містить межу перенесення на продакшн: канон ставить умову або мати стенд, максимально схожий на бойовий, або прямо описати, чим вони різняться і як переносити результат з одного на інше. Останнє не риторика — продуктивність є нелінійною функцією середовища, тож міркування «стенд удвічі слабший, отже прод витримає вдвічі більше» не є консервативною оцінкою, це просто помилка.
Три кейси про те, що робити після прогону: як від червоного графіка дійти до названого ресурсу, як назвати точку числом замість пальця й як написати абзац висновку, який інженери приймуть без дискусії. Цифри в кейсах умовні — цінність у логіці рішень, а не в значеннях.
Кейс 1. Симптом є, винного немає: спуск до ресурсу
Нічний прогін профілю «каталог плюс оформлення»: рампа з 50 до 600 за сорок хвилин. Зранку в чаті фраза «оформлення тупить», у звіті — червоний графік. Зводимо все, що є, в одну таблицю з мітками часу: без спільної осі часу порівнювати нічого.
хв VU RPS p50 p95 помилки CPU app черга виконання очікування у пулу БД
05 100 118 180 240 0,0% 22% 0 2 мс
15 250 295 195 310 0,1% 38% 0 6 мс
25 400 338 210 980 0,3% 51% 1 240 мс
35 520 341 215 2100 1,8% 54% 1 760 мс
40 600 339 220 2400 3,1% 55% 1 910 мс
Читаємо це рядок за рядком, а не як картинку.
- Медіана майже не поворухнулась,
p95виріс у десять разів. Отже, типовий користувач нічого не помітив, а деградував хвіст — і саме він дає ті 3% помилок наприкінці. Середнє за прогін тут показало б щось на кшталт «трохи гірше», тобто нічого. - стала на місці після 25-ї хвилини. Кількість віртуальних користувачів росте, RPS — ні, а росте пропорційно доданим користувачам. Це і є поведінка за перегином: далі ми не додаємо системі роботи, ми додаємо їй черги.
- Процесор застосунку на 55% нічого не спростовує. Утилізація заліза не є доказом невинності, бо ресурсом є й , і пул потоків, і локи. Черга виконання з 25-ї хвилини стоїть на одиниці й далі не росте: будь-яке ненульове насичення варте уваги, але це не воно тягне за собою час відповіді.
- Названий ресурс — пул зʼєднань до бази. Очікування вільного зʼєднання виросло з двох мілісекунд до девʼятисот — у сотні разів — і почало рости раніше, ніж будь-що інше. Гіпотеза формулюється саме так: упирається пул, а не сама база.
- Перш ніж вірити гіпотезі, її треба спробувати спростувати. Якщо час самих запитів у базі не змінився, а росло лише очікування в черзі, — це справді пул. Якщо разом із чергою зросла й тривалість запитів, ми дивимось на наслідок, а причина в базі: важкий план, лок, брак індексу.
Що робити далі за методом: спершу три метрики на кожен ресурс, потім три метрики на кожен сервіс, і лише наостанок профіль. Другим проходом дивимось три метрики RED на кожен сервіс у ланцюжку (частота, помилки за секунду, розподіл тривалості) і питаємо, чий саме сервіс тримає зʼєднання довше, ніж мав би. Третім — профіль процесу в тому самому вікні часу: flame graph покаже, у якому виклику згоряє час, аж до рядка коду. Три поверхи не переставляються місцями: профіль без названого ресурсу дає красиву картинку, з якої нічого не випливає.
Окремо — про два числа, які лякають менеджерів. Час у звіті генератора майже завжди відрізняється від часу на дашборді сервісу, бо вимір із боку клієнта і з боку сервера — різні виміри, і саме їхня різниця корисна для налагодження. Не намагайтеся їх «звести»: різниця й показує, скільки часу запит провів у мережі, у балансувальнику та в черзі перед обробником.
Кейс 2. «Ось тут перегин»: як назвати точку критерієм, а не пальцем
Той самий прогін, питання від архітектора: на скількох користувачах ми впираємось. Показувати пальцем на графік не можна — положення перегину залежить від того, як намальовані осі, і на розтягнутій шкалі точка переїде. Тому спершу фіксуємо критерій, а вже потім рахуємо число.
Найпростіший відтворюваний критерій — підгонка двох прямих: одна описує ділянку зростання, друга — полицю, а перетин і є шукана точка.
type Point = { vu: number; rps: number };
type Line = { slope: number; intercept: number };
function fitLine(points: Point[]): Line {
const n = points.length;
const sx = points.reduce((s, p) => s + p.vu, 0);
const sy = points.reduce((s, p) => s + p.rps, 0);
const sxy = points.reduce((s, p) => s + p.vu * p.rps, 0);
const sxx = points.reduce((s, p) => s + p.vu * p.vu, 0);
const slope = (n * sxy - sx * sy) / (n * sxx - sx * sx);
return { slope, intercept: (sy - slope * sx) / n };
}
function residual(points: Point[], line: Line): number {
return points.reduce((s, p) => {
const diff = p.rps - (line.slope * p.vu + line.intercept);
return s + diff * diff;
}, 0);
}
// перебираємо всі розбиття й лишаємо те, де дві прямі описують дані найкраще
export function kneeVu(points: Point[]): { vu: number; before: Line; after: Line } | null {
const sorted = [...points].sort((a, b) => a.vu - b.vu);
let best: { vu: number; before: Line; after: Line; error: number } | null = null;
for (let split = 2; split <= sorted.length - 2; split++) {
const before = fitLine(sorted.slice(0, split));
const after = fitLine(sorted.slice(split));
const error = residual(sorted.slice(0, split), before) + residual(sorted.slice(split), after);
if (best === null || error < best.error) {
const vu = (after.intercept - before.intercept) / (before.slope - after.slope);
best = { vu, before, after, error };
}
}
if (best === null) return null;
// санітарна перевірка: після зламу нахил має впасти в рази, інакше полиці немає
const flattened = Math.abs(best.after.slope) < Math.abs(best.before.slope) / 5;
return flattened ? { vu: best.vu, before: best.before, after: best.after } : null;
}
Що дивитися і чому:
- Число з коду не є істиною — воно є відтворюваним. Два аналітики на тих самих даних отримають ту саму цифру, і саме цим формальний критерій відрізняється від «ну тут же видно». На даних із першого кейса ця функція повертає 286 віртуальних користувачів, а на рівній прямій без полиці — чесний
null. У звіті пишуть обидві частини: «перегин на 286 віртуальних користувачів за критерієм перетину двох регресійних прямих». - Функція навмисно вміє повертати «полиці немає». Підгонка завжди щось знайде, навіть на прямій лінії, тому потрібна перевірка, що після зламу нахил дійсно впав у рази. Без неї ви автоматизували не аналіз, а самовпевненість.
- Формальний спосіб не один. Крім двох прямих є алгоритм Kneedle, який спирається на кривизну; вибір способу — теж частина критерію, і його теж називають у звіті.
- Часто корисніше інше число. Бізнесу зазвичай потрібен не математичний перегин, а максимум навантаження, на якому система ще вкладається в ціль: «до 330 віртуальних користувачів
p95тримається нижче за 800 мс». Це вимірюється однозначно й не залежить від масштабу осей узагалі.
Кейс 3. Абзац висновку: чотири кроки й три зняті альтернативи
Один і той самий прогін можна описати так, що зміну зроблять сьогодні, або так, що звіт піде в довгу дискусію. Різниця — у формулюваннях.
| як пишуть найчастіше | чому це не працює | як треба |
|---|---|---|
| «оформлення почало підгальмовувати» | прикметник не звіряється з ціллю й не має часу | «з 25-ї хвилини, від 400 віртуальних користувачів, p95 оформлення зріс із 310 мс до 2,4 с, помилки — з 0,1% до 3,1%» |
| «система не тягне навантаження» | не названо ні ресурс, ні сервіс, отже правити нічого | «упирається пул зʼєднань до БД: очікування зʼєднання зросло з 2 до 910 мс» |
| «на графіку видно, що після 400 користувачів усе погано» | графік не є доказом, а точка залежить від масштабу осей | «за критерієм перетину двох прямих перегин на 286 VU; ціль p95 тримається до 330 VU» |
| «треба збільшити пул» | немає ні очікуваного ефекту, ні способу перевірки | «підняти пул з 20 до 40, очікуємо зникнення черги; перевірка — повторний прогін того самого профілю» |
| «на проді буде краще, бо сервер потужніший» | лінійне перенесення на інше середовище | «стенд відрізняється кількістю реплік і обсягом даних; на прод переносимо лише напрямок, не число» |
Далі — та частина, на якій звіти провалюються найчастіше. Перш ніж звинувачувати систему, у документі мають бути зняті три альтернативи, і кожна знімається фактом, а не словом «перевірили».
- Генератор. У звіт ідуть його власні ресурси: процесор із запасом щонайменше пʼятої частини, памʼять не вище девʼяноста відсотків, мережа не в поличці, своп вимкнений. Якщо інструмент упреться в сто відсотків процесора, він потрапить у і сам домалює вам зайві сотні мілісекунд. Помилки при цьому розводять за :
socket: too many open files— вичерпані дескриптори на генераторі,read: connection reset by peer— зʼєднання скинула система під тестом,context deadline exceeded— відповідь не встигла у відведений час. - Середовище. Генератор не стоїть на сервері застосунку; прогін «на кластер» справді розкладався по вузлах, а не бив в одну машину через кеш DNS віртуальної машини Java; у прогоні не було ввімкнених важких елементів
Listener, бо більшість із них тримає копію кожного семпла, який показує. - Валідація. Відповіді перевірялися за вмістом, а не за фактом наявності. Інакше може виявитися, що останні пʼятнадцять хвилин прогону ви міряли швидкість сторінки помилки — а вона, як відомо, дуже швидка.
Останній пункт варто закривати в самому скрипті навантаження, а не постфактум:
import http from 'k6/http';
import { check } from 'k6';
export const options = {
thresholds: {
'http_req_duration{name:checkout}': ['p(95)<800'],
checks: ['rate>0.99'],
},
};
export default function () {
const payload = JSON.stringify({ cartId: 'c-42', paymentMethod: 'card' });
const res = http.post('https://staging.example.com/api/checkout', payload, {
headers: { 'Content-Type': 'application/json' },
tags: { name: 'checkout' },
});
check(res, {
'статус 200': (r) => r.status === 200,
// маркер вмісту, стійкий до порожнього тіла: без нього міряємо сторінку помилки
'у тілі є номер замовлення': (r) => (r.body ?? '').includes('"orderId"'),
});
}
Що дивитися і чому:
- Перевірка на довжину тіла ламається саме тоді, коли потрібна. Поки система здорова, вона проходить; щойно та почала падати, тіла немає — і в звіті зʼявляється помилка скрипта замість зафіксованої системи. Тому перевірки пишуть так, щоб порожня відповідь була нормальним входом, а не винятком.
- Самі по собі перевірки прогін не червонять. У k6 гейтом є пороги: без запису в
thresholdsви отримаєте гарну статистику й зелений вихідний код навіть при третині невдалих перевірок. - Тег — не косметика. Поріг, накладений на всі запити разом, розмиває деградацію одного кроку в загальній масі; поріг на іменовану транзакцію показує рівно те, про що йшлося у висновку.
- Рекомендація без способу перевірки — це побажання. Доведеною зміну робить лише повторний прогін на незмінних профілі, даних і стенді. Графік, який став гарнішим після трьох одночасних правок, не доводить нічого — ви навіть не знаєте, яка з правок подіяла.
Розподіл, зрізи й типи метрик
- Можу пояснити, чому рішення ухвалюють за розподілом, а не за середнім, і навести приклад, де середня за добу не поворухнулася, хоча 5% запитів були вдвадцятеро повільнішими.
- Знаю, на яке питання відповідає медіана (типовий випадок), а на яке —
p99чиp99.9(правдоподібне найгірше), і чому хвіст важчає саме під навантаженням. - Розумію, чому готові квантилі не агрегуються й не перераховуються під інше вікно, чим у цьому питанні гістограма відрізняється від зведення (summary) і чому
p95двох сервісів із різними кошиками звести не можна. - Розводжу лічильник (counter) і датчик (gauge): падіння лічильника — це рестарт процесу, від датчика похідну не беруть, відсутній ряд нулем не є, а
0.03на дашборді може бути тими самими «3%» зі звіту.
Метод пошуку: USE, RED, профіль
- Можу назвати порядок аналізу — симптом, ресурс, код — і пояснити, чому перелік інструментів на це питання не відповідає.
- Знаю трійку USE (утилізація, , помилки) і те, що насичення міряють чергою, а не відсотком: черги виконання, довшої за кількість ядер, і середньої черги до диска понад одиницю в показнику зайнятості процесора не видно взагалі.
- Памʼятаю, що ресурсами є й мʼютекси, ліміт задач і дескриптори файлів, а загальна утилізація ховає одне гаряче ядро від одного потоку чи привʼязаного переривання.
- Можу назвати три метрики RED на сервіс, те, що тривалість подають розподілами, а — відносно частоти запитів, і межу методу: лише сервіси, керовані запитами.
Точка насичення
- Можу пояснити, що за перегином нові вже не збільшують , а лише розтягують .
- Памʼятаю, що канонічного означення перегину для кривої навантаження немає, положення точки залежить від масштабу осей, тож її називають разом із критерієм — візуальним або формальним (Kneedle, підгонка двох прямих).
Типові вузькі місця
- Знаю чотири й те, що для «поганої обробки помилок під перевантаженням» канон називає причинами недостатні пули ресурсів, замалі черги й стеки та надто швидкі .
- Можу описати пул потоків і мʼютекс як ресурси з утилізацією й насиченням і сказати, що просити в розробки заздалегідь: час очікування в черзі пулу, метрики кешу разом із системою за ним, метрики у розбивці по базах.
- Розумію механізм каскаду (нестача процесора породжує конкурентність, далі черги, зірвані дедлайни RPC, , падіння частки влучань у кеш) і головний висновок: у момент прогону збирають датовані факти, а причинно-наслідковий ланцюг вибудовують уже потім.
- Знаю, що ємність міряють у ресурсах, а не в запитах за секунду, бо різні запити коштують по-різному й склад трафіку змінюється сам.
Деградація з часом
- Памʼятаю, що в довгому прогоні метрика — нахил, а не значення (перша година з
p95300 мс і остання з 900 мс зіллються в одну проміжну цифру звіту, а усереднювати готовіp95не можна взагалі), і що витік видно з боку системи, а не генератора. - Завжди питаю про вікно усереднення графіка утилізації: секундні сплески до ста відсотків ховаються всередині пʼятихвилинних середніх не вище 80%.
Пастки оптимізації
- Розрізняю «зняли вузьке місце» і «перенесли його» (більший пул зʼєднань упирає в базу, більше — у дескриптори) та впізнаю маскування симптому ширшими таймаутами, ретраєм і більшою чергою.
- Знаю правило доказу: поки немає повторного прогону на незмінних профілі, даних і стенді, оптимізація лишається гіпотезою.
Формат висновку і зняті альтернативи
- Можу сформулювати симптом у цифрах розподілу й у часі, назвати гіпотезою конкретний ресурс чи сервіс і перелічити три альтернативи, які знімають перед звинуваченням системи: генератор, середовище, валідація відповідей.
- Памʼятаю пороги здоровʼя генератора (щонайменше 20% простою процесора, памʼять не вище 90%, мережа не в поличці, своп вимкнено) і те, що рекомендація містить дію, очікуваний ефект, спосіб перевірки й межу перенесення на продакшн.
Квіз
Перед стартом
- Питань: 17
- Поріг «зараховано»: ≥70% правильних відповідей.
- Результат впливає на прогрес; завалені питання підуть у чергу повторення.
- Квіз впливає на компліт теми: тема стає «пройдено», лише коли прочитано теорію І квіз складено на ≥70%.
Питання
У якому порядку йде аналіз результатів прогону?