Середовище, генератори навантаження і достовірність
Зміст
«Система тримає 500 запитів за секунду» — саме по собі це не число, а половина числа. Друга половина: на якому стенді, з якими даними і яким шляхом до системи. Приберіть її — і звіт перетворюється на прогноз, який ніхто не може перевірити.
Дві найдорожчі помилки перф-тестування живуть саме тут, і жодна з них не про систему. Перша: у звіті написано «час відповіді виріс удвічі», а виріс він на генераторі, який уперся у власний процесор. Друга: «стенд удвічі слабший за прод, отже прод витримає вдвічі більше» — арифметика, яку канон знімає поіменно: продуктивність є нелінійною функцією середовища. Тому й коректне питання до стенда не «яке у вас було середовище», а «чим воно відрізнялося від продового і в який бік це зміщувало результат».
Достовірність перф-середовища: що мусить збігатися
(test environment fidelity) — це відповідь на питання «наскільки цифри зі стенда переносяться на прод». Канон вимагає зробити тестове середовище настільки близьким до продакшену, наскільки можливо, а якщо це неможливо — мати чітке розуміння відмінностей і того, як результати проєктуватимуться на продакшн. Друга половина вимоги важливіша за першу: точну копію мало хто може собі дозволити, а от назвати відмінності зобовʼязані всі.
Найважливіші частини середовища канон перелічує за важливістю: дані, конфігурація заліза й ПЗ, конфігурація мережі. Дані стоять першими не випадково:
- розмір і структура даних можуть кардинально вплинути на результати навантажувального тесту;
- мала вибірка або вибірка іншої складності дає оманливі результати, надто коли продакшн працюватиме з великим набором даних;
- передбачити, наскільки обсяг даних впливає на продуктивність, до реального тестування важко — тобто «у нас 10 тисяч замовлень замість 10 мільйонів, але пропорцію ми врахуємо» не працює;
- правило: чим ближчі до продових за розміром і структурою, тим надійніші результати (як їх роздають між користувачами — окрема глава).
Далі те, що пропускають найчастіше. Зменшене середовище потребує іншої конфігурації, а не просто менших очікувань. Якщо на тестових машинах менше фізичної памʼяті, параметри памʼяті ПЗ (наприклад, розмір heap Java) доводиться коригувати, щоб уникнути сторінкової підкачки. Інакше стенд почне свопити там, де прод не свопить, і зміряє власне налаштування.
Мережа має два зрізи. Географія: для глобальних систем один із підходів — розгорнути генератори там, де є користувачі, а для мобільних емуляція мережі лишається найбільш життєздатним варіантом через різноманіття типів мереж. І те, чого не видно в конфігах: за замовчуванням JMeter користується кешем DNS віртуальної машини Java — саме тому навантаження отримує лише один сервер кластера, і окремий елемент керування кешем DNS існує рівно для застосунків із кількома серверами за балансувальниками (CDN тощо). Сюди ж — підміна IP, тобто симуляція того, що кожен має іншу адресу: ISTQB називає її серед налаштувань інструмента, і без неї балансувальник та засоби обмеження частоти (rate limiting) бачать один клієнт, а не тисячу. Де стоїть сам генератор — теж частина середовища: виконувати JMeter на сервері застосунку можна, але це додає йому обчислювального навантаження, і результати будуть дещо зіпсовані; рекомендований варіант — машини в тому самому сегменті мережі.
Дві процедурні вимоги роблять прогони порівнюваними між собою. Якщо під час тесту дані генеруються або змінюються, початкові дані, можливо, доведеться відновити перед наступним циклом. А саме середовище й налаштування валідують до прогону — контрольованим набором тестів із перевіркою їх результату й того, що засоби моніторингу відстежують важливу інформацію; що тест працює як задумано, перевіряють аналізом журналів і вмісту бази.
import { test, expect } from '@playwright/test';
// передпольотна перевірка: той білд і ті дані, на які ми розраховували
test('стенд валідний перед прогоном', async ({ request }) => {
const version = await (await request.get('/api/version')).json();
expect(version.commit).toBe(process.env.EXPECTED_COMMIT);
const stats = await (await request.get('/api/internal/stats')).json();
expect(stats.orders).toBeGreaterThan(1_000_000);
});
І межа, яку варто знати навіть тим, хто ніколи не бачив власного стенда: прод відтворити не можна в принципі. Канон SRE каже це прямо — продакшн зазвичай не герметичний, бо темп вносить живі зміни малими зрозумілими частинами, а самі зміни можуть потрапляти в прод не в тому порядку, у якому їх додавали в систему контролю версій; висновок джерела ще різкіший: усе продове середовище навмисно не є представницьким для жодної окремої версії бінарника з репозиторію. «Prod-like» — це керована відмінність, а не досяжна рівність.
Чому не можна множити на коефіцієнт
Канонічне формулювання вже названо: продуктивність є нелінійною функцією середовища, тож чим далі середовище від продакшену, тим важче робити точні прогнози. Питання в тому, звідки береться нелінійність, — без механізму це звучить як формальність, і саме тому коефіцієнт продовжують застосовувати.
Механізм найкраще видно на канонічному прикладі (cascading failure). Якщо один кластер відмовляє, запити до сусіднього зростають до 1200 за секунду; фронтенди не здатні обробляти 1200, тож починають вичерпувати ресурси, падати, зривати дедлайни або поводитися неправильно — і в результаті частота успішно оброблених запитів опускається значно нижче за 1000. Навантаження зросло на 20%, а корисна робота впала нижче початкової. Жоден коефіцієнт такого не описує.
Вичерпання ресурсу до того ж підживлює саме себе. Нестача процесора означає, що запити обробляються довше, тож більше запитів обробляється одночасно, а це зачіпає майже все: памʼять, кількість активних потоків, дескриптори файлів і ресурси бекендів. Поруч стоїть спіраль смерті збирача сміття: менше доступного процесора → повільніші запити → більше витрат памʼяті → більше збирань сміття → ще менше доступного процесора.
І окремо — чому навіть однакове залізо не рятує, якщо інший мікс запитів. Різні запити можуть мати кардинально різні вимоги до ресурсів, а вартість запиту здатна змінюватися від довільних чинників — коду клієнта або навіть часу доби. Висновок канону сформульовано як досвід, здобутий важким шляхом: моделювання ємності як «запитів за секунду» часто виявляється поганою метрикою, і краще міряти ємність прямо в доступних ресурсах. Тобто «система тримає 5000 запитів за секунду» без складу цих запитів не переноситься нікуди — навіть на прод із тим самим залізом.
Тим часом сам навантажувальний тест канон SRE ставить першим у переліку заходів проти перевантаження: це найважливіша вправа, яку слід провести, бо без тесту в реалістичному середовищі дуже важко передбачити, який саме ресурс вичерпається і як це проявиться. Слово «реалістичному» тут несуче. Що робити з отриманими симптомами далі — тема аналізу результатів і пошуку вузьких місць.
Генератор — теж система під навантаженням
Машина, що створює навантаження, має ті самі ресурси й те саме , що й система під тестом. Коли впирається вона, тест починає міряти себе — і найгірше, що цифри при цьому виглядають як проблема системи: якщо інструмент використовує 100% процесора для , тест зазнає обмежень (throttling), і метрики результату покажуть значно більший , ніж є насправді.
Пороги здоровʼя генератора дока k6 дає числами: щонайменше 20% простою процесора (тобто до 80% зайнятості), памʼять не вище 90%, мережа не в поличці — багато машин EC2 мають зʼєднання 1 Гбіт/с, і якщо трафік постійно тримається на цій позначці, тест, імовірно, обмежений мережевою картою. Своп вимикають: підкачка на набагато повільніше сховище дає непередбачувані ефекти й найімовірніше знецінить результати, бо генератор працював по-різному в різних частинах прогону. Памʼять оцінюють заздалегідь: прості тести — приблизно 1–5 МБ на віртуального користувача, тести з вивантаженням файлів — десятки мегабайтів; спосіб порахувати — прогнати тест на 100 користувачах і помножити спожите на цільову кількість.
Контрінтуїтивна деталь: швидший сервер навантажує генератор сильніше — він повертає відповідь швидше, тож інструменту доводиться працювати більше. Поруч дока називає поіменно й наслідок неправильно підібраної кількості потоків — проблему Coordinated Omission, яка може дати хибні або неточні результати.
Як довести, що вперлися саме в генератор:
- помилки розводяться за :
socket: too many open files— це генератор досяг межі відкритих дескрипторів;read: connection reset by peer— зʼєднання скинула цільова система;context deadline exceeded— запит пішов, але система не відповіла вчасно; - генератори моніторять як систему: ISTQB вимагає цього прямо, щоб уникнути ситуації, коли навантаження не тримається належно через повільний генератор;
- ресурс — це не лише процесор і памʼять: у чеклісті USE дескриптори файлів і ліміт задач є повноцінними ресурсами з власною утилізацією й власними помилками.
Окремий клас — інструмент, що зʼїдає ресурси вимірювача. Більшість елементів Listener у JMeter тримає копію кожного семпла, який показує, тож «подивлюся дерево результатів під час прогону» перетворює тест на вимір власного інтерфейсу; канон радить протилежне — режим командного рядка, якнайменше елементів Listener і , CSV замість XML, дані для рандомізації файлом заздалегідь. У k6 те саме з іншого боку: тіло відповіді за замовчуванням вантажиться в памʼять, а кожен унікальний URL створює новий обʼєкт часового ряду.
Скільки тягне одна машина — числа з доки k6: один процес ефективно використовує всі ядра, один екземпляр тягне 30 000–40 000 одночасних користувачів, і якщо вам не треба більше за 100 000–300 000 запитів за секунду, одного екземпляра, найімовірніше, досить. Тобто розподілену генерацію треба доводити, а не оголошувати — механіка й пастки в главі про інструменти.
І остання пастка, через яку «той самий тест на тій самій машині дав інші цифри»: команди, що піднімають ліміти зʼєднань, застосовуються негайно для всієї системи й скидаються до усталених значень, якщо перезапустити мережевий сервіс або машину, а ulimit діє лише в поточній сесії оболонки.
Що стоїть між генератором і системою
Між генератором і системою зазвичай стоять вузли, які вміють відповісти замість системи або не пустити вас до неї. Поки ви їх не назвали, ви не знаєте, що саме зміряли.
CDN (Content Delivery Network) — це спільний кеш. Мережа проміжних серверів стоїть географічно ближче до користувачів і кешує відповіді, тож віддавання статики через CDN знижує навантаження запитами на власні сервери організації; такі керовані кеші (reverse proxy, CDN, service worker разом із Cache API) розгортають самі розробники сервісу, щоб розвантажити origin. Наслідок для перф-тесту прямий: якщо сценарій тягне статику, зміряна (system ) частково описує , а не систему під тестом, і директива s-maxage задає таким кешам окремий термін свіжості. Механіка — у главі Кешування; тут важливий один висновок: перед прогоном треба знати, що зі сценарію взагалі доїжджає до системи.
Обмеження частоти рахує вас одним клієнтом. Код 429 вказує, що користувач надіслав забагато запитів за проміжок часу, причому RFC свідомо не визначає, як сервер ідентифікує користувача й рахує запити; на практиці рахують за IP клієнта, а за наявності автентифікації — за користувачем чи застосунком, і саме обмеження буває як на весь сервер, так і на окремий ресурс. Заголовок Retry-After при 429 необовʼязковий, а формат його — або HTTP-дата, або кількість секунд; той самий заголовок трапляється й при 503, і при редиректі, тож «побачив Retry-After — значить ліміт» хибно.
Для прогону з цього випливає три речі. Перша: полиця пропускної здатності може належати обмежувачу, а не системі, — і виглядає вона як ідеально рівне плато. Друга: усі віртуальні користувачі одного генератора йдуть з однієї адреси, тому без підміни IP ви впираєтеся в ліміт швидше за будь-якого реального користувача. Третя: OWASP у редакції 2023 перейменував відповідний на Unrestricted Resource Consumption, змістивши акцент із самого ліміту на вичерпання ресурсів, включно з платними за виклик (лист, SMS, дзвінок) — тобто сценарій, який зачіпає такі операції, коштує грошей, і це пункт погодження прогону, а не деталь.
Межа з іншим розділом тут проходить по наміру. DoS як , WAF як засіб захисту й обмеження частоти як контроль безпеки — предмет розділу про безпеку. У цій главі те саме залізо цікавить лише як чинник, що спотворює вимір: навантаження, яке ми створюємо навмисно, — не те саме, що навантаження, яким нас атакують.
WAF як чинник спотворення виміру
Наша практика (не канон). Далі — те, як це роблять команди, з якими ми працювали; окремого стандарту чи специфікації під це немає.
Синтетичне навантаження мало схоже на людей: однакові заголовки, рівний темп, кілька адрес на тисячі сесій. Периметр — а це зазвичай WAF (web application firewall) — цілком може прочитати це як атаку й почати відсіювати запити, підкидати перевірки або притримувати темп — і далі ваш графік описує реакцію периметра, а не системи. Найгірше, що виглядає це як правдоподібна деградація: час відповіді росте, зʼявляються помилки, у системних метриках тим часом спокій.
Що з цим роблять. Прогін погоджують із тими, хто володіє периметром, і фіксують у плані, чи адреси генераторів в . Або свідомо ставлять генератор за периметр — і тоді у звіті прямо пишуть, що периметр у вимір не входив, як пишуть про будь-яку іншу відмінність середовища. Обидва варіанти законні; незаконний один — не знати, у якому з них ви перебуваєте.
Стаби замість зовнішніх залежностей
ISTQB називає цю практику поіменно: якщо якісь частини системи або даних недоступні для перф-тестів із будь-якої причини, слід реалізувати обхід — наприклад, (stub, у побуті «заглушка»), що замінює й емулює сторонній компонент, відповідальний за обробку кредитних карток. Процес часто називають (service virtualization), для нього є спеціальні інструменти, і їх використання наполегливо рекомендоване, щоб ізолювати систему під тестом. Ключове слово — ізолювати: без цього перф-тест міряє чужий сервіс замість свого.
Дублер із нульовою бреше. Gatling дає це вбудованим елементом: фіктивна дія імітує виклик віддаленої системи із заданим часом відповіді, результатом і оновленням сесії, і випадок названо прямо — коли справжню віддалену систему в навантажувальному тесті викликати не можна, але метрики бізнес-процесів усе одно потрібні. Час відповіді там задають сталим числом, виразом або випадковим значенням, а результат можна зробити невдалим. Звідси й правило: стаб має віддавати час, схожий на справжній, інакше з профілю зникає очікування, яке в проді є.
Окремий інструмент дає більше, ніж підміну однієї відповіді: WireMock описує себе як інструмент -тестування API, який допомагає створювати стабільні тестові й розробницькі середовища, ізолювати себе від нестабільних сторонніх сервісів і симулювати API, яких ще не існує. Три різні причини — і лише друга з них про перф-тест.
Найпідступніший випадок — залежність, яка мовчить. Канон SRE вимагає перевіряти саме його: тестувати некритичні бекенди й переконуватися, що їх недоступність не заважає критичним компонентам, бо запити можуть суттєво сповільнитися й тримати ресурси, чекаючи на некритичний бекенд. І окремо — як фронтенд поводиться, якщо некритичний бекенд ніколи не відповідає, наприклад поглинає запити: він не повинен масово відхиляти запити, вичерпувати ресурси чи віддавати відповіді з дуже високою затримкою. Тобто дублер потрібен не лише щоб прибрати залежність, а й щоб зімітувати її найгіршу поведінку.
І межа, яку називають у звіті: замінивши сервіс, ви перестали міряти систему цілком. Результат стосується системи з фіктивною залежністю — рівно як і будь-яка інша відмінність середовища.
Тестування на проді: коли виправдано
Спокуса зрозуміла: на проді дані правильного розміру й структури, справжній мікс запитів і справжній периметр — тобто рівно те, чого стенду бракує. Але прод не є «кращим стендом». Він не герметичний, його не можна привести у відомий стан, і повторити прогін на ньому не вийде: між двома прогонами система змінилася сама.
Канонічна форма присутності перф-тестування на проді інша — і вона не про генерацію навантаження. (scalability testing) — вид, який розбирає глава про види performance-тестів; тут ми беремо з нього один наслідок. Воно визначає масштабованість — ступінь, у якому компонент або систему можна підлаштувати під змінну ємність. Результат цього виду канон описує як експлуатаційний артефакт: щойно межі масштабованості відомі, порогові значення можна встановити й моніторити на проді, щоб отримати попередження про проблеми, які можуть ось-ось виникнути; саме продове середовище при цьому можна підлаштувати відповідною кількістю заліза. Тобто число дає лабораторія, а прод його стереже — що з цим числом робить моніторинг, розбирає глава про APM і спостережуваність.
Коли ж навантаження на проді таки виправдане — а це буває, коли ризик не відтворюється ніде інде, — розмову ведуть не в термінах «можна чи не можна», а в термінах названої ціни. Перевантаження є найпоширенішою причиною каскадних , тож прогін на проді за визначенням не є нейтральною дією; дешевшим його не робить навіть акуратність — бекенд може перевантажитися, навіть якщо переважна більшість його процесора витрачається просто на відхилення запитів. І дві вимоги, що на стенді виглядають гігієною, тут стають умовою : відновлення початкових даних після прогону й перевірка, що моніторинг збирає потрібні метрики, до старту.
Типові помилки
«Час відповіді виріс — система деградує». Виглядає як знахідка, а насправді першим виключають генератор: при 100% зайнятості процесора тест зазнає обмежень тротлінгу, і метрики покажуть значно більший час відповіді, ніж є насправді. Тому перед цією фразою у звіті має стояти графік ресурсів генератора — ISTQB і вимагає моніторити машини генерації нарівні із системою.
«У звіті too many open files — система не тримає зʼєднань». Виглядає як відмова системи, а насправді це наш бік: генератор досяг межі відкритих дескрипторів, а дескриптори файлів і ліміт задач — повноцінні ресурси з власними помилками. Скинуте зʼєднання (connection reset by peer) — навпаки, вердикт цільової системи.
«Швидший сервер розвантажує генератор». Виглядає логічно, а насправді навпаки: швидший сервер змушує інструмент працювати більше, бо повертає відповідь швидше. Звідси й сусідня пастка — неправильно підібрана кількість потоків і Coordinated Omission із хибними або неточними результатами. Того самого класу помилка — «подивлюся дерево результатів під час прогону»: більшість елементів Listener тримає копію кожного показаного семпла, і тест починає міряти власний інтерфейс.
«Ми навантажили кластер із шести вузлів». Виглядає як тест кластера, а насправді — тест одного вузла: за замовчуванням JMeter користується кешем DNS віртуальної машини Java, тож усі запити йдуть на один сервер. Дзеркальна помилка — поставити генератор на машину застосунку: так робити можна, але це додає їй навантаження, і результати будуть дещо зіпсовані.
«Стенд удвічі слабший — прод витримає вдвічі більше». Виглядає як консервативна оцінка, а насправді — заборонене міркування: продуктивність є нелінійною функцією середовища, і надійність прогнозів падає в міру того, як тестова система дедалі менше схожа на прод. «Зробимо стенд точною копією» теж не вихід: прод не герметичний і навмисно не представляє жодної окремої версії бінарника.
Підсумок
- Цифра без опису середовища — половина цифри. Канон вимагає стенда, максимально близького до проду, а якщо це неможливо — чіткого розуміння відмінностей і того, як результати проєктуватимуться на прод.
- Дані стоять першими серед частин середовища: розмір і структура кардинально впливають на результат, а передбачити цей вплив до реального тесту важко.
- Множити на коефіцієнт не можна: продуктивність є нелінійною функцією середовища, і 20% зайвого навантаження здатні опустити корисну роботу нижче початкової.
- Генератор — теж система: до 80% процесора, памʼять до 90%, мережа не в поличці, своп вимкнено; помилки розводять за походженням, а машини генерації моніторять нарівні із системою.
- Усе, що стоїть між генератором і системою, — частина виміру: edge-вузол CDN, балансувальник, обмеження частоти, периметр і кожен стаб.
Можливі питання
«Чим ваше тестове середовище відрізнялося від продового?» Слухають не перелік заліза, а вміння назвати відмінності й напрямок їх впливу: обсяг і структура даних, конфігурація, мережа, зовнішні залежності. Відповідь «у нас усе було як на проді» читається як «ми не перевіряли».
«Стенд удвічі слабший за прод. Скільки витримає прод?» Пастка на арифметику: така екстраполяція заборонена, бо продуктивність нелінійно залежить від середовища. Далі очікують, що ви запропонуєте зміряти межу, а не вивести коефіцієнт.
«Час відповіді в тесті виріс. Ваші дії?» Перевіряють порядок: спершу ресурси генератора й походження помилок, і лише потім метрики системи. Хто одразу йде до бекенду, пропускає найчастішу причину хибної тривоги.
«Як ви тестували систему, що ходить у платіжний шлюз?» Очікують згадку віртуалізації сервісів і двох її умов: стаб віддає реалістичний час відповіді, а у звіті сказано, що саме замінено. Окремий випадок для сеньйора — залежність, яка не падає, а мовчить.
«Чи можна ганяти навантаження на проді?» Питання не про сміливість, а про ціну й альтернативу. Сильна відповідь: канонічний вихід перф-тесту на прод — це пороги з відомих меж масштабованості під моніторингом, а не генератор; а якщо навантаження на проді неминуче, називають ризик, спосіб відкату даних і готовий до старту моніторинг.
Джерела
Достовірність перф-середовища: що мусить збігатися
- ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — §4.2.8: вимога до близькості середовища, три його найважливіші частини, роль обсягу даних, коригування конфігурації на зменшеному стенді, відновлення даних між циклами, валідація стенда, емуляція мережі й підміна IP.
- Google SRE Book — Chapter 17: Testing for Reliability — продакшн не є герметичним і навмисно не представляє жодної окремої версії бінарника.
- Apache JMeter — Remote (Distributed) Testing — чому генератор не ставлять на сервер застосунку і чому генератори тримають у тому самому сегменті мережі.
- Apache JMeter — User's Manual: Component Reference (таймери, екстрактори, асерти, набір даних) — кеш DNS віртуальної машини Java і чому без керування ним навантаження отримує один сервер кластера.
Чому не можна множити на коефіцієнт
- Google SRE Book — Chapter 22: Addressing Cascading Failures — приклад 1000 → 1200 запитів за секунду з падінням корисної роботи нижче початкової, ланцюжок вичерпання ресурсів і спіраль смерті збирача сміття.
- Google SRE Book — Chapter 21: Handling Overload — різна вартість запитів, «запити за секунду» як погана метрика ємності й вимір ємності в доступних ресурсах.
- ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — §4.2.8: продуктивність як нелінійна функція середовища й вимога описати, як результати проєктуватимуться на продакшн.
Генератор — теж система під навантаженням
- Grafana k6 — Running large tests — тротлінг генератора й завищений час відповіді, пороги процесора, памʼяті й мережі, оцінка памʼяті на користувача, розведення помилок за походженням, скидання лімітів ОС після перезапуску, ємність одного екземпляра.
- Apache JMeter — Best Practices — швидший сервер навантажує генератор сильніше, Coordinated Omission, настанови про режим запуску, елементи
Listener, асерти й формат результатів. - Apache JMeter — Introduction to listeners — більшість елементів
Listenerтримає копію кожного показаного семпла. - ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — §4.2.8: вимога моніторити й самі машини генерації навантаження.
- Brendan Gregg — USE Method: Linux Performance Checklist — дескриптори файлів і ліміт задач як ресурси з власною утилізацією й помилками.
Що стоїть між генератором і системою
- MDN Glossary — CDN — CDN як мережа серверів у багатьох локаціях і зниження навантаження запитами на власні сервери організації.
- MDN — HTTP caching — керовані кеші (reverse proxy, CDN, service worker) і навіщо їх розгортають.
- RFC 9111 — HTTP Caching — спільний кеш проти приватного й окремий термін свіжості
s-maxageдля спільних кешів. - RFC 6585 — Additional HTTP Status Codes — значення
429і свідома відмова специфікації визначати, як сервер рахує запити. - MDN — 429 Too Many Requests — рахунок за IP або за автентифікованим користувачем, обмеження на сервер або на окремий ресурс.
- MDN — Retry-After header — два формати заголовка й інші коди, при яких він приходить.
- OWASP API Security Top 10 — 2023 (перелік ризиків) — перейменування ризику на Unrestricted Resource Consumption і акцент на вичерпанні ресурсів, включно з платними за виклик.
Стаби замість зовнішніх залежностей
- ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — §4.2.8: стаб замість недоступного стороннього компонента, термін «service virtualization» і рекомендація ізолювати систему під тестом.
- Gatling — Scenario — фіктивна дія із заданим часом відповіді й результатом замість виклику недоступної системи.
- WireMock — Overview (офіційна документація) — стабільні середовища, ізоляція від нестабільних сторонніх сервісів і симуляція API, яких ще немає.
- Google SRE Book — Chapter 22: Addressing Cascading Failures — вимога тестувати некритичні бекенди й поведінку фронтенду, коли бекенд ніколи не відповідає.
Тестування на проді: коли виправдано
- ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — §1.2: межі масштабованості як пороги для моніторингу на проді й підлаштування продового середовища залізом; §4.2.8: відновлення даних і валідація моніторингу до прогону.
- ISTQB Glossary — означення масштабованості й тестування масштабованості.
- Google SRE Book — Chapter 22: Addressing Cascading Failures — перевантаження як найпоширеніша причина каскадних відмов.
- Google SRE Book — Chapter 21: Handling Overload — сервіс може перевантажитися навіть тоді, коли процесор іде на відхилення запитів.
- Google SRE Book — Chapter 17: Testing for Reliability — продакшн не герметичний, тож у відомий стан його не привести.
Що таке достовірність тестового середовища (test environment fidelity)?
Це міра того, наскільки висновок, зроблений на стенді, має право поширюватися на продакшн. Канон ставить дві вимоги одразу: тримати стенд настільки схожим на бойове середовище, наскільки дозволяють гроші й час, а якщо схожості немає — уміти перелічити відмінності й пояснити, як результат проєктується на прод. Друга вимога на практиці важить більше: точну копію прода фінансують одиниці, а от список розбіжностей зобовʼязана мати кожна команда. Тому поруч із числом у звіті стоїть паспорт стенда: , обсяг і структура даних, залізо, топологія мережі, перелік замінених залежностей. Без цього паспорта цифру не можна ні перевірити, ні повторити — вона перетворюється на обіцянку під чесне слово. На питання «чим ваш стенд відрізнявся від прода» відповідь «нічим» читається як «ми не дивилися».
Які частини середовища найважливіші і чому дані стоять першими?
Порядок за важливістю такий: дані, конфігурація заліза й програмного забезпечення, конфігурація мережі. Дані очолюють список, бо їхній обсяг і структура здатні змінити результат до невпізнання: на таблиці в десять тисяч рядків і на таблиці в десять мільйонів — це різні плани, різна робота індексів і різний профіль дискових операцій. Найнеприємніше те, що вплив обсягу неможливо надійно передбачити до самого прогону — тобто спроба зміряти на маленькій вибірці й «врахувати пропорцію» не має під собою підстав. Правило просте: чим ближча тестова база до продової за розміром і за формою, тим більше довіри до цифр. Мала або спрощена вибірка не робить результат «оптимістичним на стільки-то відсотків» — вона робить його непорівнянним. Роздача цих даних між — окрема тема кореляції й параметризації, але вона починається вже після того, як обсяг зійшовся.
Стенд удвічі слабший за прод. Скільки витримає прод?
Це класична пастка на арифметику, і правильна відповідь — «з цих даних не виводиться». Продуктивність є нелінійною функцією середовища, тож множення на коефіцієнт заборонене як метод. Механізм видно на канонічному прикладі (cascading failure): коли сусідній кластер відмовляє й потік запитів зростає з тисячі до тисячі двохсот за секунду, фронтенди не просто починають працювати повільніше — вони вичерпують ресурси, зривають дедлайни й падають, а частка успішно оброблених запитів опускається нижче за початкову тисячу. Двадцять відсотків додаткового навантаження зʼїли більше, ніж дали. Так само працює вичерпання будь-якого ресурсу: брак процесора подовжує обробку, через це росте кількість одночасних запитів, а за нею — памʼять, потоки, дескриптори й ресурси бекендів. Тому на таке питання сильний кандидат пропонує не формулу, а експеримент: зміряти межу на середовищі, максимально близькому до цільового, і описати різницю.
А якщо просто зробити стенд точною копією продакшену?
Не вийде — і не через бюджет. Канон SRE фіксує це прямо: прод не є герметичним, бо в нього безперервно котяться дрібними порціями, а зміни здатні дістатися бойового середовища не в тому порядку, у якому потрапляли в систему контролю версій. Наслідок різкий: продове середовище навмисно не відповідає жодній окремій версії бінарника з репозиторію. Тобто «копія прода» — це копія стану, якого не існує як фіксованої точки. Практичний висновок для тестувальника: prod-like — це керована відмінність, а не досяжна рівність, і завдання полягає не в тому, щоб відмінності знищити, а в тому, щоб їх назвати й оцінити напрямок їх впливу на результат.
На тестових машинах менше памʼяті, ніж на проді. Що з цим робити?
Головна помилка тут — просто занизити очікування й лишити конфігурацію такою самою. Зменшене середовище потребує іншого налаштування, а не тих самих параметрів «на меншому залізі». Якщо фізичної памʼяті менше, доводиться коригувати параметри памʼяті самого програмного забезпечення — наприклад, розмір heap Java, — інакше стенд почне вивалюватися у сторінкову підкачку там, де прод не свопить. У такому прогоні ви зміряєте не застосунок, а власне налаштування: графік покаже провали, яких у бойовому середовищі не буде взагалі. Тому пункт «конфігурацію ПЗ підігнано під розмір стенда» стоїть у списку відмінностей нарівні з обсягом даних, і саме його зазвичай забувають задокументувати.
Що ви робите зі стендом до старту прогону?
Два кроки, які роблять прогони порівнюваними між собою. Перший — валідація середовища й налаштувань контрольованим набором тестів: перевіряють не тільки те, що система відповідає, а й що результати цих тестів правильні і що засоби моніторингу справді збирають потрібні показники. Що тест поводиться так, як задумано, підтверджують уже після старту — аналізом журналів і вмісту бази, а не тим, що звіт зелений. Другий крок — дані: якщо сценарій під час прогону створює або змінює записи, початковий стан бази, найімовірніше, доведеться відновити перед наступним циклом, інакше другий прогін піде по іншій базі, ніж перший. Найдешевший спосіб виявити «не той білд, не ті дані» — передпольотний тест на кілька секунд, який звіряє версію застосунку з очікуваним і перевіряє порядок обсягу ключових таблиць.
Де має стояти сама машина-генератор?
Рекомендація — окрема машина в тому самому мережевому сегменті, що й система під тестом. Запустити прямо на сервері застосунку технічно можливо, але це додає серверу обчислювального навантаження, і результати виходять частково зіпсованими: ви міряєте систему, у якої частину процесора забрав ваш інструмент. Місце генератора — теж частина опису середовища, а не побутова дрібниця. Для глобальних систем є окремий підхід: генератори розгортають у тих регіонах, де живуть користувачі, щоб у вимір увійшла реальна мережева . Для мобільного трафіку канон вважає найбільш життєздатною емуляцію мережі — типів мобільних мереж надто багато, щоб відтворювати їх залізом.
Ви навантажили кластер із шести вузлів, а завантажений виявився один. Що сталося?
Найімовірніше — кеш DNS. JMeter із коробки покладається на DNS-кеш віртуальної машини Java, тому імʼя хоста перетворюється на адресу один раз, і далі всі віртуальні користувачі йдуть туди ж, скільки б вузлів не стояло за балансувальником. Окремий елемент керування цим кешем існує рівно для випадку, коли застосунок розгорнутий на кількох серверах за балансувальниками — включно з CDN. Симптом упізнаваний: у звіті уперлася в стелю значно раніше за очікування, а моніторинг кластера показує один розжарений вузол і пʼять сплячих. Це не системи й не її межа ємності — це помилка конфігурації прогону, і висновок про ємність кластера з такого прогону робити не можна.
Навіщо потрібна підміна IP-адрес віртуальних користувачів?
Тому що без неї тисяча віртуальних користувачів виглядає для інфраструктури як один клієнт із дуже дивною поведінкою. Підміна IP — симуляція того, що кожен віртуальний користувач приходить з іншої адреси, — стоїть у каноні серед налаштувань інструмента, і наслідок її відсутності формулюється просто: балансувальник і засоби обмеження частоти (rate limiting) бачать один клієнт, а не тисячу. Найпомітніше це бʼє по обмежувачу: лічильник зазвичай ведуть за адресою клієнта, тож ваші запити почнуть відсікати раніше, ніж це сталося б із тисячею реальних користувачів. У звіті це проявиться як 429 і помилки, які легко прийняти за межу системи. Тому підміна IP — не «тонке налаштування для великих прогонів», а умова коректності будь-якого тесту, що йде через балансувальник або периметр.
Які пороги здоровʼя генератора ви тримаєте в голові?
Орієнтири прості: щонайменше пʼята частина процесора має лишатися вільною (тобто зайнятість не вище приблизно вісімдесяти відсотків), памʼять не заходить за девʼяносто відсотків, а мережевий інтерфейс не стоїть у поличці — багато машин мають зʼєднання 1 Гбіт/с, і якщо трафік постійно тримається на цій позначці, тест, імовірно, уперся в мережеву карту, а не в систему. Своп на генераторі вимикають: щойно памʼять починає вивалюватися на значно повільніший диск, поведінка інструмента стає непередбачуваною, і прогін втрачає цінність — у різних своїх частинах він працював по-різному. Памʼять оцінюють заздалегідь: прості сценарії — приблизно один-пʼять мегабайтів на віртуального користувача, сценарії з вивантаженням файлів — десятки мегабайтів; надійний спосіб порахувати — прогнати сотню користувачів, зміряти спожите й помножити на цільову кількість. Ці числа не самоціль: вони потрібні, щоб під час розбору звіту одразу було видно, чи мав генератор запас.
Як довести, що вузьким місцем був генератор, а не система?
Трьома незалежними доказами. Перший — ресурси самої машини навантаження за той самий інтервал: якщо процесор стояв під стелею, інструмент зазнавав обмежень (throttling), і метрики результату показують значно більший , ніж є насправді, — картина виглядає як деградація сервісу, хоча сервіс ні до чого. Другий — розкладка помилок за : socket: too many open files означає, що на генераторі скінчилися дескриптори файлів і система цих запитів навіть не бачила; read: connection reset by peer — зʼєднання розірвала цільова сторона; context deadline exceeded — запит дійшов, а вчасної відповіді не було. Третій — сам факт того, що машини генерації моніторяться нарівні з системою: канон вимагає цього прямо, щоб не отримати прогін, у якому навантаження просто не трималося через повільний генератор. І ресурсом тут вважається не лише процесор із памʼяттю: дескриптори файлів та межа кількості задач — такі самі ресурси, у яких є своя утилізація й свої характерні помилки.
Чому кажуть, що швидший сервер навантажує генератор сильніше?
Бо генератор працює у зворотному звʼязку із системою: чим швидше приходить відповідь, тим швидше треба надіслати наступний запит, розібрати тіло, перевірити й записати семпл. Повільний сервер, парадоксально, дає інструменту перепочинок — потоки більшу частину часу чекають. Практичний наслідок неприємний: після успішної оптимізації бекенда той самий прогін на тій самій машині може почати впиратися в генератор, і «покращення» обернеться погіршенням цифр. Сусідня пастка того ж класу — неправильно підібрана кількість потоків: вона веде до проблеми Coordinated Omission, а та дає хибні або неточні результати. Тому кількість потоків підбирають під конкретну машину й перевіряють на запас ресурсів, а не призначають один раз назавжди.
Інструмент вимірювання сам зʼїдає ресурси. Де це трапляється найчастіше?
Класика — спостерігати за прогоном через графічний інтерфейс. Більшість елементів Listener у JMeter зберігає копію кожного семпла, який показує, тож відкрите дерево результатів під час тесту перетворює прогін на вимір власного інтерфейсу. Канонічні настанови радять протилежне: запуск у режимі командного рядка, мінімум елементів Listener і асертів, CSV замість XML для результатів, дані для рандомізації підготовлені файлом заздалегідь. У k6 те саме приходить з іншого боку: тіло відповіді за замовчуванням потрапляє в памʼять, а кожен унікальний URL створює окремий обʼєкт часового ряду — тому прогін по адресах із випадковими ідентифікаторами роздуває споживання памʼяті без жодної користі для звіту. Спільний висновок: усе, що інструмент робить «для зручності перегляду», оплачується з ресурсів тієї самої машини, чиї межі ви щойно домовилися не переходити.
Один і той самий сценарій на одній і тій самій машині дав інші цифри. Що перевірити першим?
Найчастіша побутова причина — ліміти операційної системи, які хтось піднімав руками. Команди, що збільшують дозволену кількість зʼєднань, діють негайно на всю систему, але скидаються до усталених значень після перезапуску мережевого сервісу або самої машини, а ulimit узагалі живе лише в поточній сесії оболонки — нова сесія CI отримає стару межу. Симптом упізнаваний: перший прогін чистий, наступний дає купу помилок про дескриптори, хоча в репозиторії нічого не змінилося. Тому підняття лімітів роблять частиною підготовки машини в скрипті прогону, а не разовою дією руками, і фіксують у паспорті середовища. Інші кандидати з того ж списку — новий білд системи, підмінені дані, увімкнений своп і сусід по віртуалізації, але почати варто з лімітів, бо це найдешевше перевірити.
Ви ганяєте сценарій, що тягне статику. Що насправді міряє така пропускна здатність?
Частково — CDN (Content Delivery Network), а не вашу систему. CDN — це мережа проміжних серверів ближче до користувачів, яка кешує відповіді й тим самим знімає частину запитів із власних серверів організації; спільні керовані кеші такого типу — reverse proxy, CDN, service worker із Cache API — ставить сама команда сервісу, щоб зняти частину роботи з origin. Для перф-тесту це означає, що частина вашого трафіку до системи просто не доїжджає, а числа пропускної здатності описують чужу інфраструктуру. Окрема деталь: спільним кешам термін свіжості задають директивою s-maxage, тож поведінка edge-вузла може відрізнятися від того, що бачить браузер. Практичне правило перед прогоном — знати, яка частка запитів сценарію завершується на edge-вузлі, і або свідомо винести статику зі сценарію, або описати її внесок у звіті.
Пропускна здатність уперлася в стелю, а в розкладці помилок — тисячі 429. Чия це межа?
Це вердикт обмежувача частоти, а не ємність системи. Код 429 означає, що клієнт надіслав забагато запитів за проміжок часу, причому специфікація свідомо не визначає, як саме сервер ідентифікує клієнта й рахує запити: на практиці лічильник ведуть за IP, а там, де є автентифікація, — за обліковим записом чи застосунком; сам ліміт буває як загальний для сервера, так і окремий для конкретного ресурсу. Оскільки всі віртуальні користувачі одного генератора приходять з однієї адреси, без підміни IP ліміт наздоганяє вас раніше, ніж наздогнав би живого користувача, — тобто до системи доїжджає лише частина навантаження, і про її межу такий прогін не каже нічого. Заголовок Retry-After тут не є надійним індикатором: при 429 він необовʼязковий, приходить у двох форматах (HTTP-дата або кількість секунд) і трапляється також при 503 та редиректах, тож «бачу Retry-After — значить ліміт» хибно. Ще один пункт того ж розділу: у редакції OWASP 2023 перейменували на Unrestricted Resource Consumption, змістивши акцент на вичерпання ресурсів, зокрема платних за виклик — лист, SMS, дзвінок. Сценарій, що зачіпає такі операції, коштує реальних грошей, і це предмет погодження прогону, а не технічна дрібниця.
Час відповіді росте, помилки зʼявляються, а системні метрики спокійні. Що це може бути?
Одразу застереження: окремого стандарту чи специфікації під цю тему немає — далі практика команд, а не канон. Дуже характерна картина для периметра — зазвичай це WAF (web application firewall). Синтетичне навантаження погано вдає людей: заголовки в усіх однакові, темп рівний, а тисячі сесій приходять із кількох адрес — периметр цілком здатен розцінити це як й узятися фільтрувати запити, підсовувати додаткові перевірки чи штучно сповільнювати темп. Далі ваш графік чесно описує реакцію периметра, а не поведінку застосунку, і саме тому системні метрики мовчать: до застосунку трафік просто не дійшов. Роблять із цим одне з двох. Або прогін погоджують із власниками периметра й фіксують у плані, чи внесені адреси генераторів до . Або свідомо ставлять генератор за периметр і прямо пишуть у звіті, що периметр у вимір не входив, — рівно так само, як фіксують будь-яку іншу розбіжність середовища. Обидва шляхи прийнятні; неприйнятно лише не розуміти, у якому з двох станів ви зараз.
Як тестувати систему, яка ходить у платіжний шлюз?
Через (service virtualization). Якщо якась частина системи або даних недоступна для перф-тестів, канон вимагає реалізувати обхід — наприклад, (stub), що заміщує й емулює сторонній компонент обробки карткових платежів; для цього існують спеціальні інструменти, і їх використання наполегливо рекомендують, щоб ізолювати систему під тестом. Ключове слово тут — ізолювати: без стаба ви міряєте чужий сервіс разом зі своїм і не можете відповісти, чий саме час відповіді виріс. Але дублер із нульовою затримкою бреше не менше: у Gatling для цього є окрема фіктивна дія — вона вдає звернення до віддаленої системи, і їй задають тривалість, підсумок і оновлення сесії; випадок описано прямо — реальний сервіс у прогоні чіпати не можна, а метрики бізнес-процесу все одно потрібні. Тривалість там ставлять фіксованим числом, обчислюваним виразом або випадковою величиною, а підсумок за потреби роблять невдалим. Правило звідси: стаб мусить віддавати затримку, схожу на реальну, інакше профіль втрачає ту паузу, яка в бойовому середовищі нікуди не дінеться. І останнє — у звіті пишуть, що саме замінено: результат стосується системи з фіктивною залежністю, а не системи цілком.
Найпідступніший випадок — залежність, яка не падає, а мовчить. Що з нею робити?
Перевіряти її окремо, бо мовчання гірше за відмову. Канон SRE вимагає тестувати некритичні бекенди й переконуватися, що їхня недоступність не заважає критичним компонентам: запити здатні суттєво сповільнитися й тримати ресурси, поки чекають на відповідь, якої не буде. Окремий сценарій — коли некритичний бекенд не відповідає ніколи, наприклад мовчки поглинає запити: фронтенд у такій ситуації не має права ані сипати відмовами всім підряд, ані виїдати власні ресурси, ані відповідати з надзвичайно довгою затримкою. Тобто дублер ставлять не лише щоб прибрати чужий сервіс із виміру, а й щоб відтворити його найгірший сценарій — очікування, яке не завершується ніколи. На співбесіді це той рівень відповіді, який відрізняє «ми замокали платіжку» від «ми перевірили, що станеться, якщо платіжка перестане відповідати, не повертаючи помилки». Готові інструменти на кшталт WireMock описують себе саме як спосіб отримати стабільне середовище, відгородитися від несталих зовнішніх сервісів і підняти макет API, якого ще не існує, — три різні задачі, і лише друга з них про перф-тест.
Чи можна ганяти навантаження на проді?
Спокуса зрозуміла: там правильні дані, справжній мікс запитів і живий периметр — тобто рівно те, чого бракує стенду. Але прод не є «кращим стендом»: він не герметичний, його не привести у відомий стан, і повторити прогін неможливо, бо між двома запусками система змінилася сама. Канонічна форма присутності перф-тестування в бойовому середовищі взагалі інша й не повʼязана з : (scalability testing) у лабораторії дає межі, а вже за цими межами встановлюють порогові значення й моніторять їх на проді, щоб отримати попередження про проблему до того, як вона стане інцидентом; саме продове середовище при цьому підлаштовують відповідною кількістю заліза. Якщо ж навантаження на проді все-таки неминуче — ризик не відтворюється більше ніде, — розмову ведуть не про сміливість, а про названу ціну: перевантаження є найпоширенішою причиною каскадних відмов, а бекенд здатен захлинутися навіть тоді, коли майже весь його процесор іде на відхилення запитів. Умови тут ті самі, що на стенді виглядають гігієною: відновлення початкових даних після прогону й перевірена до старту повнота моніторингу.
Три ситуації, у яких відповідь «система тримає стільки-то» стоїть або не стоїть на ногах: паспорт стенда, який перетворює число на перевірюваний факт; розслідування стелі, яку поставила не система; і платіжного , який або рятує вимір, або тихо його підробляє. Скрізь — що дивитися і чому саме це.
Кейс 1. Паспорт стенда: перетворюємо число на перевірюваний факт
Менеджер просить одну цифру для релізного рішення: «скільки тримає прод». Прогін був на стенді. Замість цифри в чат іде цифра разом із таблицею відмінностей — і саме таблиця робить її придатною до вжитку.
| Що порівнюємо | Стенд | Прод | Куди зміщує результат |
|---|---|---|---|
| Обсяг основної таблиці | 1.2 млн замовлень | 78 млн замовлень | стенд завищує: інші плани запитів, індекси ще в памʼяті |
| Вузли застосунку | 2 × 4 vCPU / 8 ГБ | 6 × 8 vCPU / 32 ГБ | напрямок невідомий: інша топологія, не масштаб |
heap Java | 3 ГБ (підігнано під 8 ГБ RAM) | 12 ГБ | стенд занижує на довгих прогонах: частіші збирання сміття |
| Мікс запитів | 70% читання / 30% запису | 55% / 45% за метриками місяця | стенд завищує: запис дорожчий |
| Платіжний шлюз | стаб, фіксовані 40 мс | зовнішній сервіс, p95 ≈ 900 мс | стенд завищує: з профілю зникло очікування |
| Периметр (WAF) | генератор стоїть за ним | увесь трафік іде крізь нього | стенд завищує: периметр у вимір не входив |
| Статика | тягнеться зі свого origin | віддається з edge-вузлів CDN | стенд занижує: на проді частина запитів до системи не доїде |
Що дивитися і чому:
- Колонка «куди зміщує» важливіша за колонку «прод». Канон вимагає не рівності середовищ, а розуміння відмінностей і того, як результат проєктується на бойове середовище. Рядок без напрямку зміщення — це визнання «ми не думали», а не документація.
- Питання «помножимо на три, бо вузлів утричі більше?» має одну правильну відповідь — ні. Продуктивність нелінійно залежить від середовища, і саме тут це видно предметно: три рядки таблиці тягнуть результат угору, два — вниз, а один взагалі не має знака. Множник, який мав би все це підсумувати, не існує.
- Рядок про дані стоїть першим не з ввічливості. Обсяг і структура здатні змінити результат кардинально, а передбачити цей вплив до реального прогону не виходить — тобто саме цей рядок і робить решту таблиці приблизною.
- Рядок про
heapJava — той, який забувають. Зменшений стенд потребує іншої конфігурації ПЗ, а не тих самих параметрів на слабшому залізі; підігналиheap— зобовʼязані про це написати, бо профіль збирача сміття тепер інший, ніж на проді. - Правильна форма відповіді менеджеру. Не «прод витримає N», а «на стенді межа була N при такому міксі й такій базі; на проді очікуємо іншу, і щоб її знати, треба зміряти на середовищі, ближчому за трьома рядками таблиці». Далі — розмова про бюджет, а не про арифметику.
Перед самим прогоном таблицю підпирають передпольотним тестом: він займає секунди й ловить найдорожчу помилку — прогін по не тому або не тих даних.
import { test, expect } from '@playwright/test';
test.describe('передпольотна перевірка стенда', () => {
test('білд, дані й моніторинг відповідають паспорту прогону', async ({ request }) => {
const build = await (await request.get('/api/version')).json();
expect(build.commit, 'на стенді не той білд, який збиралися міряти')
.toBe(process.env.EXPECTED_COMMIT);
// обсяг даних — перший рядок паспорта, тож перевіряємо порядок, а не точне число
const stats = await (await request.get('/api/internal/stats')).json();
expect(stats.orders).toBeGreaterThan(1_000_000);
// моніторинг має збирати метрики ДО старту, а не «розберемося потім»
const metrics = await (await request.get('/metrics')).text();
expect(metrics).toContain('http_server_requests_seconds');
});
});
Останній — не формальність: канон вимагає валідувати середовище й налаштування контрольованим набором тестів і окремо переконуватися, що засоби моніторингу справді відстежують потрібне. Прогін, під час якого зʼясувалося, що метрик бекенда ніхто не збирав, доведеться повторити цілком.
Кейс 2. Стеля пропускної здатності: хто насправді поставив межу
Звіт нічного прогону виглядає як знайдена межа системи. Насправді таких «меж» у прогоні буває три, і всі вони не про систему.
Прогін 02:10–02:50, ramping 0 → 2000 VU, сценарій catalog+checkout
throughput ......... 848–852 rps упродовж 26 хвилин (розкид менше 0.5%)
http_req_failed .... 11.4%
p(95) .............. 3.9s
Розкладка помилок:
HTTP 429 ................................. 6 940
read: connection reset by peer .............. 512
socket: too many open files ................... 0
Машина-генератор:
CPU ..... 41% RAM ..... 55% NIC ..... 180 Mbit/s з 1 Gbit/s
Що дивитися і чому:
- Спершу знімаємо підозру з генератора — і тут вона знімається. Процесор має запас, памʼять далеко від девʼяноста відсотків, мережа не в поличці, дескрипторних помилок нема. Якби процесор стояв під стелею, розмова була б інша: при 100% процесора тест зазнає обмежень , і метрики покажуть значно більший , ніж є насправді. Порядок дій незмінний — генератор виключають першим, а не останнім.
- Висновок робить розкладка помилок, а не форма кривої. «Так рівно система не поводиться» — здогад, а не доказ: сама по собі стабільна лінія нікого не називає. Тому поруч із графіком завжди кладуть перелік помилок за — саме він показує, хто зупинив запити.
- Майже сім тисяч
429називають винуватця. Обмеження частоти рахує клієнта за IP, а всі одного генератора приходять з однієї адреси — тобто ліміт спрацював на «одному клієнті» з нереальною поведінкою, а не на тисячі різних. Без підміни IP цей прогін узагалі не міг дійти до межі системи.
Перевірка займає один запит — і одразу видно, чий це вердикт:
curl -s -o /dev/null -D - "https://stage.example.com/api/catalog?page=2"
HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-Cache: MISS
429тут — вердикт обмежувача, а не системи. Специфікація свідомо не описує, як сервер ідентифікує клієнта й веде рахунок: одні лічать за IP, інші за автентифікованим користувачем чи застосунком, і ліміт буває як загальний для сервера, так і окремий для ресурсу. Тому «підняли ліміт на 30%» без розмови з власником сервісу — вгадування.Retry-Afterне є доказом ліміту. При429він необовʼязковий, приходить у двох форматах (кількість секунд або HTTP-дата) і трапляється також при503і при редиректах. Читати треба код, а заголовок брати лише як підказку про паузу.X-Cacheу відповіді — друга межа того самого прогону. Частина сценарію завершується на edge-вузлі CDN: він віддає статику з кешу й знімає ці запити з власних серверів організації. Значить, у звіті частково описує чужу інфраструктуру, аMISSна цьому конкретному запиті означає, що саме він до системи таки дійшов. Назви й формат таких діагностичних заголовків залежать від провайдера (X-Cache,CF-Cache-Statusтощо), тож звірятися треба з його докою, а не з памʼяттю. Перед прогоном треба знати часткуHIT— інакше незрозуміло, яку систему ви зміряли.- Третій кандидат на «межу» не видно в жодному заголовку — і канону під ним немає. Далі практика команд, а не стандарт: якщо системні метрики спокійні, а помилки й ростуть, підозра переходить на периметр — WAF цілком може прочитати рівний синтетичний трафік з кількох адрес як й почати притримувати темп. Перевіряється це не в , а розмовою: чи внесені адреси генераторів до і чи стоїть генератор перед периметром узагалі.
Правильний наступний крок — не баг у трекері, а повторний прогін з іншими налаштуваннями: підміна IP, узгоджені з власником ліміти для тестових адрес, свідоме рішення щодо статики. Висновок про ємність системи робиться лише з прогону, у якому жоден із трьох сторонніх обмежувачів не спрацював.
Кейс 3. Стаб платіжного шлюзу: два способи підробити вимір
Сценарій оформлення замовлення ходить у зовнішній платіжний шлюз. Ганяти навантаження в чужий бойовий сервіс не можна, тож ставимо стаб (stub) — і саме тут роблять дві протилежні помилки.
| Варіант стаба | Що віддає | Що стає зі звітом |
|---|---|---|
| Стаба немає, ходимо в справжній шлюз | реальні затримки чужого сервісу | міряємо чужу систему разом зі своєю; чий саме p95 виріс — невідомо |
| Стаб із нульовою затримкою | 200 за 1–2 мс | очікування зникло з профілю; пул зʼєднань і потоки виглядають здоровими, у проді захлинуться |
| Стаб із реалістичною затримкою | 200 за 700–1100 мс, ~2% відмов | профіль близький до бойового: видно, скільки потоків зайнято очікуванням |
| Стаб, який мовчить | не відповідає взагалі | найцінніший прогін: перевіряємо, чи витримає система залежність, що зависла |
Що дивитися і чому:
- Мета стаба — ізоляція, а не швидкість. Канон називає практику і рекомендує її саме для того, щоб відокремити систему під тестом від зовнішніх компонентів. Прискорення профілю — побічний ефект, і він шкідливий: інструменти дозволяють виставити тривалість фіксованим числом, обчислюваним виразом або випадковою величиною, а підсумок зробити невдалим саме тому, що дублер має бути схожим на оригінал, а не зручним.
- Другий рядок таблиці — найпоширеніша помилка. Стаб на 1 мс прибирає з тесту головне, що дає зовнішня залежність: зайняті ресурси, які чекають. У проді сотні потоків стоятимуть на очікуванні платежу, а ваш звіт про це не знає нічого.
- Четвертий рядок — рівень сеньйора. Канон SRE вимагає перевіряти саме мовчазну залежність: некритичний бекенд, який ніколи не відповідає, здатен тримати ресурси й сповільнювати критичні шляхи, а фронтенд при цьому не має права ані сипати відмовами всім підряд, ані виїдати власні ресурси, ані відповідати з надзвичайно довгою затримкою.
Мовчазну залежність зручно відтворити на рівні браузерного тесту — прямо перевіряємо, що інтерфейс не помирає разом із чужим сервісом:
import { test, expect } from '@playwright/test';
test('інтерфейс переживає платіжний шлюз, який не відповідає', async ({ page }) => {
// залежність не падає, а мовчить: запит іде і не повертається ніколи
await page.route('**/api/payments/**', async () => {
await new Promise(() => {}); // навмисно без resolve
});
await page.goto('/checkout');
await page.getByTestId('place-order').click();
// критичний шлях лишається живим: користувач бачить стан, а не завислий екран
await expect(page.getByTestId('payment-pending')).toBeVisible({ timeout: 15_000 });
await expect(page.getByTestId('order-summary')).toBeVisible();
// і застосунок сам обриває очікування, а не тримає його вічно
await expect(page.getByTestId('payment-timeout-notice')).toBeVisible({ timeout: 45_000 });
});
- Тест перевіряє не платіжку, а поведінку системи навколо неї. Зелений результат означає, що застосунок має власний і деградує керовано; червоний — що одна мовчазна залежність здатна вичерпати ресурси на критичному шляху. Це знахідка, якої звичайний навантажувальний прогін із добрим стабом не дає ніколи.
- Той самий сценарій має сенс і на протокольному рівні. У перф-прогоні мовчазну залежність тримають частку часу (наприклад, останні десять хвилин довгого тесту) і дивляться на та кількість активних потоків, а не на час відповіді — саме там проявляється утримання ресурсів.
- І фінальний рядок у звіті. Замінивши шлюз дублером, ви перестали міряти систему цілком: результат стосується конфігурації з фіктивною залежністю. Це така сама відмінність середовища, як менша база чи слабше залізо, і місце їй — у паспорті стенда з кейсу 1.
Достовірність середовища
- Знаю, що вимога канону подвійна: або стенд, близький до прода, або чіткий перелік відмінностей з поясненням, як результат проєктується на бойове середовище; звідси й правило «цифра йде у звіт разом із паспортом стенда».
- Можу назвати частини середовища в порядку важливості — дані, конфігурація заліза й ПЗ, конфігурація мережі — і пояснити, чому дані очолюють список: їхній вплив до реального прогону надійно не передбачається, тож «зміряємо на малій вибірці й врахуємо пропорцію» не працює.
- Розумію, що зменшений стенд потребує іншої конфігурації ПЗ (наприклад, підігнаного розміру
heapJava), а не просто менших очікувань, — і що цю правку записують окремим рядком серед відмінностей, бо профіль збирача сміття тепер інший. - Знаю два процедурні кроки, без яких прогони непорівнювані: валідація середовища й засобів моніторингу контрольованим набором тестів до старту та відновлення початкових даних перед наступним циклом, якщо сценарій дані змінює.
Чому коефіцієнт заборонений
- Можу сформулювати правило разом із механізмом: продуктивність нелінійно залежить від середовища, і приріст навантаження з тисячі до тисячі двохсот запитів за секунду здатен опустити корисну роботу нижче за початкову тисячу, бо вичерпання ресурсу підживлює саме себе аж до спіралі смерті збирача сміття.
- Розумію, чому однакове залізо не рятує при іншому міксі: вартість запитів різна й змінюється від довільних чинників, тому кількість запитів за секунду — погана метрика ємності, а міряти краще прямо в доступних ресурсах.
Генератор — теж система під навантаженням
- Тримаю в голові пороги здоровʼя генератора: щонайменше 20% простою процесора, памʼять не вище 90%, мережевий інтерфейс не в поличці, своп вимкнено.
- Знаю, що робить генератора зі звітом — завищує проти справжнього, — і тому ресурси машини навантаження дивлюся раніше за метрики системи.
- Розводжу помилки за :
too many open files— межа генератора,connection reset by peer— зʼєднання розірвала система,context deadline exceeded— система не відповіла вчасно; і памʼятаю, що дескриптори файлів та межа кількості задач — такі самі ресурси зі своєю утилізацією. - Можу пояснити контрінтуїтивне: швидший сервер навантажує генератор сильніше, а невдало підібрана кількість потоків веде до Coordinated Omission із хибними або неточними результатами.
- Знаю, чим інструмент зʼїдає власні ресурси (елементи
Listenerу JMeter тримають копію кожного показаного семпла; у k6 тіло відповіді за замовчуванням лежить у памʼяті, а на кожну унікальну адресу заводиться окремий часовий ряд) і чому підняті ліміти ОС зникають після перезапуску, аulimitживе лише в поточній сесії оболонки.
Що стоїть між генератором і системою
- Можу пояснити, чому CDN робить частину виміру чужою: віддає відповідь із кешу, тож частково описує його, а спільним кешам термін свіжості задає
s-maxage. - Знаю, що
429у розкладці помилок — межа обмежувача, а не системи, і що специфікація свідомо не визначає спосіб рахунку (за IP або за автентифікованим користувачем); памʼятаю, щоRetry-Afterпри429необовʼязковий і трапляється також при503та редиректах. - Знаю дві конфігураційні пастки маршруту: без підміни IP балансувальник і обмежувач бачать одного клієнта замість тисячі, а кеш DNS віртуальної машини Java відправляє весь трафік на один вузол кластера.
- Можу назвати WAF як чинник спотворення (синтетичний трафік читається як , системні метрики при цьому спокійні) і два законні виходи: узгоджений адрес або свідоме рішення стати за периметр із записом про це у звіті — памʼятаю, що канону під цю тему немає, це практика команд. Окремо тримаю в голові ціну прогону: у редакції OWASP 2023 названо Unrestricted Resource Consumption, тож сценарій, що чіпає операції з оплатою за виклик (надісланий лист, SMS, дзвінок), узгоджують заздалегідь.
Стаби й зовнішні залежності
- Можу назвати практику терміном — (service virtualization) — і сформулювати її мету одним словом: ізолювати систему під тестом від недоступного стороннього компонента.
- Знаю, чому дублер із нульовою бреше: профіль втрачає паузу, яка в проді залишається, тож має віддавати реалістичний час відповіді, а за потреби й невдалий результат.
- Перевіряю не лише , а й мовчання: залежність, що зависає назавжди, тримає ресурси й сповільнює критичні шляхи; і памʼятаю, що заміна залежності — така сама відмінність середовища, яку пишуть у звіті.
Прод
- Розумію, чому прод не є «кращим стендом»: він не герметичний, навмисно не відповідає жодній окремій версії бінарника, у відомий стан не приводиться, а отже прогін на ньому не повторити.
- Знаю канонічну форму присутності на проді — межі масштабованості міряють у лабораторії, а за ними ставлять пороги моніторингу — і дві умови , якщо навантаження на проді неминуче: спосіб відкату даних і перевірений до старту моніторинг.
Квіз
Перед стартом
- Питань: 17
- Поріг «зараховано»: ≥70% правильних відповідей.
- Результат впливає на прогрес; завалені питання підуть у чергу повторення.
- Квіз впливає на компліт теми: тема стає «пройдено», лише коли прочитано теорію І квіз складено на ≥70%.
Питання
Достовірність тестового середовища (test environment fidelity) — що це?