Кореляція, параметризація і тестові дані
Зміст
Записали сценарій, поставили 200 — і всі ітерації зелені, з часом відповіді 40 мілісекунд. Виглядає як чудова продуктивність. Насправді система з другого запиту віддає «сесія недійсна», а ви щойно зміряли, як швидко вона генерує сторінку помилки. Випадок не екзотичний: канон ISTQB називає неправильну кореляцію або її відсутність основною причиною, чому записані скрипти не відтворюються.
Ця глава — про чотири речі між «скрипт запускається» і «цифрам можна вірити»: динамічні значення з відповідей, свої дані в кожного віртуального користувача, стан прогону (авторизація й кеш) і перевірки, які роблять зміряний час осмисленим. Механіку конкретного інструмента тут не розбираємо: інструменти — у сусідній главі, стенд і генератори — у наступній.
Кореляція: значення, яких у скрипті бути не може
Кореляція (correlation) — це заміна записаних внутрішніх ідентифікаторів, що визначають контекст, на змінні, значення яких дістають із відповідей на попередні запити. Канонічний приклад — ідентифікатор користувача, отриманий при вході, який далі треба передавати в усіх наступних . Одразу й застереження про омонім: у цьому контексті слово має інший сенс, ніж у статистиці, де воно означає звʼязок між двома чи більше речами.
Чому без неї нічого не працює: записаний трафік містить значення конкретної сесії — ідентифікатор кошика, токен форми, номер замовлення. При повторі сервер бачить ідентифікатор, якого вже не існує, і відповідає помилкою. Просунуті інструменти можуть робити частину кореляції автоматично, тож у простих випадках вона буває непомітною, але в складніших потрібна ручна кореляція або нові правила кореляції — тобто «інструмент усе зробив» перевіряють, а не припускають.
Механіка кореляції та перевірки — одна: перевірки роблять дві речі — верифікують, що відповідь відповідає очікуванням, і захоплюють елементи з відповіді. той самий, із шести кроків: задати тип перевірки, витягти, перетворити, валідувати, назвати, зберегти. Кореляція — це він з увімкненим останнім кроком.
Три пастки витягу, які нічого не кажуть уголос
- Номер збігу. У JMeter нуль означає, що інструмент вибере збіг випадково, додатне
N—N-й збіг. У Gatling усталений перший: якщо крок витягу не визначено явно, виконується неявнийfind, що бере перше або єдине можливе входження. Різні усталені значення в різних інструментах — готове джерело сценарію, що «інколи падає». - Значення за замовчуванням. У JMeter якщо не збігся, змінна отримує значення за замовчуванням, і дока пояснює навіщо: без нього важко відрізнити, чи вираз не збігся, чи елемент узагалі не спрацював, чи використано не ту змінну. Зворотний бік того самого механізму — поломка стає тихою.
- Збереження лише при успіху. У Gatling збереження діє тільки якщо перевірка успішна — тобто збіглася з відповіддю й пройшла валідацію. Звідси головний симптом теми: провалена кореляція ламає наступний запит, а не той, у якому сталася.
Чим витягати
| Механізм | Коли доречний |
|---|---|
Регулярний вираз — застосовує Perl-подібний вираз до тіла відповіді; мусить містити щонайменше одну пару дужок для захоплення (крім випадку групи $0$) | Коли значення не обрамлене сталим текстом |
| Межі ліворуч і праворуч — витяг за лівою й правою межею | Коли обрамлення стале: дешевше й читабельніше |
| JSONPath / JMESPath — витяг із JSON-відповідей | Для API |
Дві деталі коштують грошей на великому прогоні. Дока Gatling попереджає: JSONPath не має належної специфікації, тож поведінка різних реалізацій відрізняється, тоді як JMESPath має повну граматику й набір тестів відповідності — звідси класичне «в евалюаторі працює, а в тесті ні». І сам витяг не безкоштовний: підрядок ефективніший за регулярний вираз для перевірки наявності, а режим «тіло без екранування» в JMeter помітно впливає на продуктивність.
Остання пастка зʼявляється, коли генератор перестає бути один. У розподіленому прогоні Gatling за замовчуванням розкладають між усіма вузлами, тож крок «залогінитися одним користувачем і роздати всім токен» ламається: цього користувача виконає лише один вузол, а решта лишиться без токена. Як саме розкладається прогін, кожен інструмент вирішує по-своєму, тож перш ніж переносити однокроковий логін на кілька машин, це перевіряють у його доці.
Параметризація: у кожного віртуального користувача свої дані
(parameterization) — це заміна фіксованих або записаних даних значеннями зі списку можливих варіантів, щоб кожен віртуальний користувач працював зі своїм набором даних. Формально канон називає кореляцію частиною параметризації скрипта; на практиці їх розводять за джерелом значення: там воно з відповіді системи, тут — зі списку, підготовленого заздалегідь.
Головне твердження теми канон формулює симетрично: запускати багато віртуальних користувачів з одним іменем і доступом до одного набору даних — простий спосіб отримати оманливі результати. Механізмів два, і діють вони в протилежних напрямках:
- дані можуть бути повністю закешовані (скопійовані з диска в памʼять для швидшого доступу), і результати будуть значно кращими, ніж у продакшені, де їх читали б із диска;
- ті самі користувачі або дані можуть спричинити проблеми конкурентності — наприклад, дані заблоковані, поки користувач їх оновлює — і результати будуть значно гіршими, ніж у продакшені.
Тобто прогін на одному користувачі — не оптимістична й не песимістична оцінка, а просто недостовірна. Скільки треба різних значень, канон числом не називає: «належний» тут означає достатньо різні, щоб уникнути проблем із і конкурентністю, і це специфічно для системи, даних і вимог тесту.
Окремий клас — дані, унікальні за побудовою. Є випадки, коли дані мусять бути параметризовані, щоб тест працював більш ніж один раз: якщо назва замовлення мусить бути унікальною, без параметризації тест упаде, щойно спробує створити замовлення з наявною записаною назвою. Це поломка другого прогону, а не першого. Генерувати такі значення на льоту канон радить не поспішати: це дорого по процесору й памʼяті, тож дані створюють заздалегідь, а за потреби «випадкові» дані з файлу комбінують із параметром прогону — наприклад, конкатенацією, що набагато дешевше.
// унікальність за побудовою: сталий префікс прогону + значення зі списку
const orderName = `${runId}-${nameFromFile}`;
Дві механіки роздачі
| Механізм | Семантика |
|---|---|
| Набір даних із файлу (JMeter) | Читає рядки з файлу й розкладає їх у змінні; файл відкривається один раз, і кожен потік бере інший рядок, причому рядки читаються на початку кожної ітерації |
SharedArray (k6) | Обʼєкт-масив, що ділить памʼять між віртуальними користувачами: функція виконується один раз, результат зберігається в памʼяті один раз, а на запит елемента користувач отримує копію |
Три припущення тут не справджуються. Порядок, у якому рядки потрапляють до потоків, залежить від порядку їх виконання, і може відрізнятися між ітераціями — «третій потік дістане третього користувача» не гарантовано. Поведінка на кінці файлу налаштовується, варіантів три: перечитувати з першого рядка (усталено), виставити всім змінним значення <EOF> або зупинити потік — усталений тихо перетворює довгий прогін на ходіння по колу. І режим спільного доступу визначає, скільки різних користувачів реально бере участь: усталено файл спільний для всіх потоків, але буває свій на групу, свій на потік чи спільний за ідентифікатором.
У SharedArray граблі свої й теж мовчазні: він лише для читання і конструюється лише в контексті ініціалізації (дані з відповіді системи туди не потрапляють), а вигоду знімають .filter() і .map() на готовому масиві, серіалізація масиву цілком і повернення його з функції підготовки. Названа й межа користі: приблизно до 1000 рядків вигоди в памʼяті майже немає, а від 10 тисяч рядків економія памʼяті починає переходити і в економію процесора — числа дока називає ілюстративними.
І ручна операція розподіленого прогону: у JMeter файли даних керівна машина не розсилає, тож для унікальних ідентифікаторів набір ділять між файлами даних вузлів.
Авторизація в перф-сценаріях
Авторизація в перф-скрипті — не «ще один запит», а стан із власними межами. Межі нижче показані на життєвому циклі перф-скрипта у k6; в іншому інструменті стадії називаються інакше, але питання лишаються ті самі. Перша межа стадійна: код ініціалізації не може робити HTTP-запитів, і причина названа прямо — це гарантує відтворюваність стадії між прогонами, бо відповідь на протокольний запит динамічна й непередбачувана. Тож «залогінитися й дістати токен» тут зробити не можна: це робота стадії підготовки. А те, що підготовка повернула, роздається копіями: передати можна лише дані (тобто JSON), функції — ні, і кожен віртуальний користувач отримує свіжу копію.
Друга межа ітераційна, і саме вона ламає найбільше сценаріїв: дійшовши до кінця функції, віртуальний користувач повертається на початок, і в межах цього перезапуску куки очищаються, а TCP-зʼєднання можуть бути розірвані. «Залогінився раз і ходить увесь тест» без окремих заходів не виходить — механіка кук і сесій працює тут як у функціональному тесті, тільки помножена на кількість користувачів.
Третя межа операційна. У підготовки власний (типово 60 секунд), і якщо вона його перевищить, тест падає ще до старту віртуальних користувачів — окремий клас падінь, який не є системи. Поруч друга пастка: якщо функція підготовки завершується аварійно, функція не викликається, тож дані невдалого прогону лишаються в системі — а канон нагадує, що перед наступним циклом може знадобитися відновити початкові дані. У Gatling межа дзеркальна: хуки до й після прогону є, але користуватися SDK в них не можна.
Наша практика (не канон). Далі — те, як це роблять команди, з якими ми працювали; окремого стандарту чи специфікації під це немає. Вхід у систему тримають окремою транзакцією: якщо кожна ітерація починається з логіна, ви навантажуєте сервіс автентифікації сильніше, ніж передбачає профіль, і цифри решти кроків стають несумісними з реальністю. Токени намагаються отримати заздалегідь — у підготовці або поза прогоном, готовим пулом облікових записів, — а логін додають у профіль у тій частці, у якій він трапляється в реальних даних. І окремо перевіряють, що час життя токена перекриває прогін: масове протухання токенів у графіках виглядає точно як деградація системи.
Кеш: холодний прогін проти прогрітого
Кеш дає на одне питання дві різні відповіді, і жодна з них не є правильною за замовчуванням. Механізм канон називає в темі даних: якщо тестовий набір малий, дані можуть бути повністю закешовані — скопійовані з диска в памʼять для швидшого доступу, — і результати будуть значно кращими, ніж у продакшені, де ті самі дані читалися б із диска. Прогрітий прогін на маленькому наборі — не «оптимізм», а вимір іншої системи.
Тому вибір між холодним і прогрітим прогоном варто робити свідомо й називати у звіті. Канон SRE радить пробувати обидві форми виходу на навантаження: і плавну рампу, і різкий сплеск, бо через ефекти кешування поступове нарощування може дати інші результати, ніж миттєвий вихід на очікуваний рівень. Це найкоротше пояснення, чому «ми ж міряли той самий сценарій» аргументом не є.
Друга половина теми — коректність, а не швидкість. Якщо ви навантажуєте сервіс зі станом або з кешуванням, тест має відстежувати стан між взаємодіями й перевіряти правильність під високим навантаженням — саме там зазвичай проявляються тонкі помилки конкурентності. Кеш, який під навантаженням віддає чужі дані, у метриках виглядає краще за справний. Механіка самого HTTP-кешування — тема глави про кешування.
Валідація відповідей: без перевірок ви міряєте швидкість помилки
Перф-тест без перевірки вмісту міряє швидкість сторінки помилки — і робить це переконливо, бо помилки віддаються швидко й стабільно. Звичний мінімум перевірок перф-сценарію — код відповіді плюс вміст, і тонкість у другій половині: якщо крок валідації не визначено явно, виконується неявний exists, тобто «щось знайшлося», а не «значення правильне».
Найважливіше твердження теми стосується не , а моменту, коли тест став цікавим: у великих стрес-тестах скрипт не повинен нічого припускати про HTTP-відповідь. Приклад — перевірка довжини тіла: вона працює, поки система не перевантажена й повертає нормальні відповіді, але коли система починає падати, працює не так, як очікується, бо припускає, що тіло є завжди, — а якщо сервер відмовляє, тіла може не бути. Результат — помилка в самому скрипті замість зафіксованої відмови системи, тобто дані втрачаються там, де вони найцінніші.
// крихка перевірка: припускає, що тіло є завжди
const okFragile = body.length > 1000;
// стійка: спершу факт відповіді, потім її вміст
const okResilient = status === 200 && typeof body === 'string' && body.includes('Замовлення №');
Три речі змінюють не код, а звіт. тривалості перевіряє, що кожна відповідь надійшла в межах заданого часу, і будь-яка відповідь, довша за задану кількість мілісекунд, позначається як невдала — повільна відповідь потрапляє в , а не лише в хвіст розподілу. За замовчуванням шаблон в асерті відповіді працює в багаторядковому режимі, тобто метасимвол . не збігається з переносом рядка, і регістр теж важливий — дві найчастіші причини «асерт не спрацював, хоча текст на екрані є». І кардинальність: більшість перевірок з умовою на очікуване значення за замовчуванням логують фактичне значення при провалі, що може дати дуже високу кардинальність метрик помилок — на масовій відмові звіт розсипається на тисячі рядків.
Звідси компроміс, який не люблять називати вголос. Настанови JMeter радять уживати якомога менше асертів, а k6 попереджає, що результат кожної окремої перевірки й групи записується окремо, тож за великої їх кількості варто подумати про видалення заради продуктивності. Це не «перевірки шкідливі»: без перевірок тест дешевший і брехливіший. Межа — перевіряти те, що відрізняє успішну транзакцію від невдалої, а не кожен елемент сторінки. Частка помилок і пороги — теми метрик та вимог до продуктивності.
Реалістичність даних: тест на порожній базі бреше
Коли перелічують, що на стенді мусить збігатися з продакшеном, дані називають першими — раніше за конфігурацію заліза й мережі. І одразу пояснюють, чому це не дрібниця: розмір і структура даних можуть кардинально вплинути на результати навантажувального тесту, а використання малої вибірки або вибірки іншої складності може дати оманливі результати, особливо коли продакшн працюватиме з великим набором даних. І головне: передбачити, наскільки обсяг даних впливає на продуктивність, до реального тестування важко. Тобто «у нас тисяча замовлень замість мільйона, але пропорцію ми врахуємо» не працює — враховувати нема з чого. Правило просте: чим ближчі до продакшенових за розміром і структурою, тим надійніші результати, причому структура важить не менше за розмір — мільйон однакових записів не є тим самим мільйоном, що в проді.
Два процесні наслідки легко пропустити. Перший: якщо під час тесту дані генеруються або змінюються, може знадобитися відновити початкові дані перед наступним циклом — інакше другий прогін міряє вже іншу базу, і порівняння з базовою лінією втрачає сенс. Другий: перед виконанням тестів середовище й налаштування мають бути валідовані — контрольованим набором тестів із перевіркою їх результату й роботи засобів моніторингу, а щоб переконатися, що тест працює як задумано, використовують аналіз журналів і перевірку вмісту бази. Решта складників достовірності стенда — у главі про середовище й генератори.
Типові помилки
«Інструмент усе скорелював сам». Виглядає як зручність, а насправді автоматика покриває прості випадки — у складніших потрібна ручна кореляція. Ознака — сценарій, який відтворюється «залежно від дня».
«Тест зелений, значить кореляція працює». Виглядає як доказ, а насправді підставлене значення за замовчуванням робить поломку тихою: змінна є, запит іде, сервер відповідає помилкою.
«Даних вистачить — у файлі десять тисяч рядків». Виглядає як запас, а насправді на кінці файлу типово вмикається перечитування з першого рядка, і прогін іде по колу тими самими користувачами.
«Додамо перевірок на кожен елемент — надійніше». Виглядає як ретельність, а насправді кожна перевірка записується окремо й коштує ресурсів генератора, а логування фактичних значень на масовій відмові роздуває кардинальність метрик помилок.
«На стенді швидше, ніж на проді, — запас є». Виглядає як хороша новина, а насправді малий набір даних міг поміститися в памʼять, тоді як прод читає з диска. Це не запас, а інший вимір.
Підсумок
- Кореляція — це значення з відповіді, параметризація — значення зі списку. Перше лікує невідтворюваність скрипта, друге — недостовірність результатів.
- Зламана кореляція валить наступний запит, а не той, у якому сталася, а значення за замовчуванням робить поломку тихою.
- Один користувач на всіх спотворює результати в обидва боки: кеш зробить цифри кращими за реальні, блокування — гіршими. Це не консервативна оцінка, а недостовірна.
- Без перевірки вмісту тест міряє швидкість помилки, а перевірка під happy path ламається саме тоді, коли система почала падати.
- Дані стоять першими серед частин стенда, і вплив їхнього обсягу важко передбачити до реального тесту.
Можливі питання
«Що таке кореляція в навантажувальному тестуванні?» Інтервʼюер слухає, чи не почнете ви говорити про статистичний звʼязок величин. Сильна відповідь — означення через механізм плюс наслідок: без кореляції записаний скрипт не відтворюється.
«Скрипт падає на другому кроці. Що робите?» Перевіряють хід думки: подивитися відповідь попереднього запиту, знайти динамічне значення, перевірити номер збігу й значення за замовчуванням — і лише потім думати про систему.
«Навіщо параметризувати дані, якщо тест і так проходить?» Дивляться, чи бачите ви обидва напрямки спотворення — кешування й конкурентність — і чи згадаєте дані, унікальні за побудовою: без них тест зелений рівно один раз.
«Як переконатися, що ви міряєте саме успішну операцію?» Слухають про перевірку коду відповіді разом із вмістом і про стійкість перевірки до відсутнього тіла; згадка про ціну перевірок читається як досвід.
«Стенд показує кращі цифри, ніж прод. Чому це не привід радіти?» Малий набір даних міг поміститися в памʼять, стан кешів залежить від форми виходу на навантаження, а вплив обсягу даних до реального тесту передбачити важко. Правильний фінал — назвати відмінності стенда, а не множити результат на коефіцієнт.
Джерела
Кореляція: значення, яких у скрипті бути не може
- ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — означення кореляції, застереження про омонім зі статистикою, кореляція як основна причина невідтворюваності записаних скриптів, межа автоматичної кореляції.
- Apache JMeter — User's Manual: Component Reference (таймери, екстрактори, асерти, набір даних) — номер збігу й випадковий вибір при нулі, значення за замовчуванням і його призначення, механізми витягу, ціна режимів роботи з тілом відповіді.
- Gatling — Checks — перевірка як верифікація плюс захоплення, шість кроків пайплайна, неявний
find, збереження лише при успішній перевірці, підрядок проти регулярного виразу, JSONPath проти JMESPath. - Gatling — Injection — розподіл профілю між вузлами й наслідок для сценарію з одним ініціалізаційним користувачем.
Параметризація: у кожного віртуального користувача свої дані
- ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — означення параметризації, симетричне спотворення результатів кешуванням і конкурентністю, критерій «достатньо різні», дані, унікальні за побудовою.
- Apache JMeter — User's Manual: Component Reference (таймери, екстрактори, асерти, набір даних) — набір даних із файлу, негарантований порядок рядків, три поведінки на кінці файлу, режими спільного доступу, ціна генерації значень під час прогону.
- Grafana k6 — SharedArray — спільна памʼять і копія на запит, обмеження «лише для читання» та контексту ініціалізації, способи втратити економію, межі користі за обсягом даних.
- Apache JMeter — Best Practices — підготовка даних заздалегідь як спосіб зменшити витрати ресурсів генератора.
- Apache JMeter — Remote (Distributed) Testing — файли даних не розсилаються вузлам, поділ набору між вузлами задля унікальності.
- Grafana k6 — Test lifecycle — заборона HTTP в ініціалізації та її причина, підготовка як місце входу в систему, передавання даних копіями, скидання користувача й очищення кук між ітераціями, власний таймаут підготовки, відсутність прибирання після аварійної підготовки.
- Gatling — Simulation — хуки до й після прогону та заборона користуватися SDK всередині них.
- ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — відновлення початкових даних перед наступним циклом тестування, коли тест їх змінює.
Кеш: холодний прогін проти прогрітого
- ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — повне кешування малого набору даних як причина результатів, значно кращих за продакшен.
- Google SRE Book — Chapter 22: Addressing Cascading Failures — порада пробувати і плавну рампу, і різкий сплеск через ефекти кешування; перевірка правильності під високим навантаженням для сервісів зі станом або кешем.
Валідація відповідей: без перевірок ви міряєте швидкість помилки
- Gatling — Checks — перевірка коду відповіді разом із вмістом у зведеному прикладі, неявний
exists, логування фактичного значення й кардинальність метрик помилок. - Apache JMeter — User's Manual: Component Reference (таймери, екстрактори, асерти, набір даних) — асерт тривалості й позначення повільної відповіді як невдалої, усталені багаторядковий режим і чутливість до регістру в асерті відповіді.
- Apache JMeter — Best Practices — рекомендація вживати якомога менше асертів.
- Grafana k6 — Running large tests — вимога нічого не припускати про відповідь, приклад із перевіркою довжини тіла, ціна окремого запису кожної перевірки.
- Google SRE Book — Chapter 22: Addressing Cascading Failures — перевірка правильності під високим навантаженням для сервісів зі станом або кешем.
Реалістичність даних: тест на порожній базі бреше
- ISTQB® CT-PT — Foundation Level Specialist Syllabus, Performance Testing v1.0 (2018) — дані першими в переліку найважливіших частин тестового середовища, вплив розміру й структури даних, непередбачуваність цього впливу до тестування, відновлення початкових даних між циклами, валідація середовища контрольним набором тестів.
Що таке кореляція (correlation) у навантажувальному тестуванні?
Це підміна «прибитих» у записі ідентифікаторів змінними, які скрипт наповнює значеннями з попередніх відповідей сервера. Записаний трафік завжди тягне сліди конкретного сеансу — ключ сесії, номер кошика, токен форми, ідентифікатор користувача, виданий при вході, — і всі вони мусять їхати далі в наступних . При повторному прогоні сервер цих значень не впізнає, бо вони належали давно закритому сеансу, і відповідає помилкою. Тому кожне таке поле вчаться діставати з тіла відповіді на льоту й підставляти в наступний запит. Окремо варто одразу зняти омонім: у перф-контексті слово не має нічого спільного зі статистичною залежністю величин, це суто механічна операція над скриптом. І саме крива кореляція або її відсутність — головна причина, чому записаний сценарій не відтворюється; ISTQB називає це прямо, тож відповідь без цієї фрази виглядає неповною.
Чим кореляція відрізняється від параметризації тестових даних (parameterization)?
Джерелом значення. Кореляція бере його з відповіді самої системи — тобто до старту прогону такого значення не існує в природі. Параметризація бере його зі списку, зібраного заздалегідь: файл із логінами, набір артикулів, пул номерів замовлень. Формально канон уважає кореляцію частиною параметризації скрипта, але практична межа важливіша, бо ці дві речі лікують різні хвороби. Без кореляції скрипт просто не відтворюється: другий запит уже падає. Без параметризації він відтворюється чудово й видає красиві цифри, яким не можна вірити. Тобто перше — про працездатність сценарію, друге — про достовірність результату.
Чому провалену кореляцію майже завжди помічають не там, де вона сталася?
Бо витяг і перевірка в перф-інструменті — це одна механіка, і збереження значення привʼязане до успіху перевірки, а не до факту виконання запиту. У Gatling перевірка робить дві речі водночас: верифікує відповідь і захоплює з неї елементи, а у неї один на шість кроків — обрати тип перевірки, витягти, перетворити, провалідувати, назвати, зберегти. Звичайний — це той самий пайплайн без останнього кроку, кореляція — з ним. Звідси і симптом: якщо витяг нічого не знайшов або валідація не пройшла, збереження не відбувається, і змінна лишається порожньою чи тримає значення з попередньої ітерації. Запит, у якому зламався витяг, при цьому цілком успішний — він отримав нормальну відповідь, просто ви з неї нічого не взяли. Помирає наступний крок, який пішов у систему з чужим або порожнім ідентифікатором. Практичний висновок для розбору: побачивши масові помилки на кроці N, першою відкривайте відповідь кроку N-1, а не логи сервера.
Що таке номер збігу в екстракторі й чому усталені значення тут небезпечні?
Номер збігу каже, яке саме входження брати, коли шаблон знайшов кілька. У JMeter додатне N означає N-й збіг, а нуль — що інструмент вибере збіг випадково. У Gatling явного кроку витягу може не бути взагалі: тоді відпрацьовує неявний find, який бере перше чи єдине входження. Небезпека не в самих значеннях, а в тому, що вони різні: сценарій, перенесений між інструментами або зібраний із чужого прикладу, поводиться інакше, ніж очікує автор. Найгірший практичний вияв — саме нуль у JMeter: скрипт стабільно зелений на демонстрації і «інколи падає» в нічному прогоні, бо щоразу тягне інший ідентифікатор. Тому номер збігу задають явно навіть тоді, коли впевнені, що збіг один.
Тест зелений, помилок нуль, але кореляція не працює. Як таке можливо?
Через значення за замовчуванням. Коли вираз у JMeter не знайшов збігу, у змінну лягає заздалегідь прописана заглушка — і механізм цей корисний: без нього неможливо відрізнити три різні ситуації, коли вираз не збігся, коли елемент узагалі не спрацював і коли в запиті використали не ту змінну. Зворотний бік у того самого механізму неминучий: змінна завжди існує, підстановка завжди відбувається, запит завжди йде. Сервер відповідає — швидко й стабільно, бо це відповідь про помилку, — а прогін без перевірки вмісту рахує її успіхом. Тому саме по собі «зелено» кореляцію не підтверджує: підтверджує її перевірка на витягнуте значення плюс контроль вмісту наступної відповіді.
Чим витягати значення: регулярний вираз, межі чи JSONPath?
Вибір робиться за формою відповіді й за ціною. універсальний і потрібен там, де значення не обрамлене сталим текстом; у JMeter він мусить містити щонайменше одну групу захоплення, і єдиний виняток — випадок групи $0$. Витяг за лівою й правою межею дешевший і читабельніший, але живе рівно доти, доки обрамлення стабільне. Для API природний JSONPath або JMESPath, і між ними є неочевидна різниця: у JSONPath немає належної специфікації, тому реалізації розходяться в деталях — звідси класичне «в онлайн-евалюаторі вираз працює, а в прогоні ні», — тоді як у JMESPath граматика описана повністю й до неї додається набір тестів на відповідність. Ціна теж важлива на масштабі: для простої перевірки наявності підрядок ефективніший за регулярний вираз, а окремі режими роботи з тілом відповіді в JMeter помітно бʼють по продуктивності. На великому прогоні це вже не мікрооптимізація: екстрактор виконується на кожному запиті кожного .
Навіщо параметризувати дані, якщо тест і так проходить?
Бо прогін одним користувачем на одному наборі даних — простий спосіб отримати оманливі результати, і спотворення йде одразу в два боки. Якщо набір маленький, він може повністю осісти в памʼяті: дані копіюються з диска й читаються звідти, тож цифри виходять значно кращими за продакшен, де ті самі рядки довелося б піднімати з диска. Одночасно ті самі облікові записи чи ті самі рядки провокують конкурентність: поки один віртуальний користувач їх оновлює, вони заблоковані для решти, і результат виходить значно гіршим за продакшен. Тобто це не консервативна й не оптимістична оцінка — це просто недостовірна оцінка, з якої не можна зробити жодного висновку. Слабка відповідь на співбесіді зупиняється на ; сильна називає обидва напрямки й додає, що вони можуть діяти в одному прогоні, тому напрямок сумарного спотворення заздалегідь невідомий.
Скільки різних значень «достатньо» для параметризації?
Числа тут немає, і це правильна відповідь, а не ухиляння. Канон формулює критерій якісно: значень має бути достатньо різних, щоб не спрацювало ані повне кешування набору, ані конкуренція за ті самі записи, а конкретна кількість залежить від системи, характеру даних і вимог тесту. Практично це означає, що обсяг набору обґрунтовують, а не вгадують: дивляться на розмір робочої множини відносно памʼяті сервера й на те, які саме рядки система блокує під час операції. Гіпотезу можна й перевірити — прогнати той самий профіль на помітно більшому наборі й порівняти : якщо цифри поїхали, набір був замалий. Відповідь «у нас тисяча рядків, вистачить» без цих міркувань читається як брак досвіду.
Що таке дані, унікальні за побудовою, і чому вони ламають саме другий прогін?
Це дані, які система вимагає неповторними: назва замовлення, номер договору, email реєстрації. Записаний скрипт містить конкретне значення, яке вже потрапило в базу під час запису, тому наступний прогін упирається в те, що такий запис уже існує. Симптом упізнаваний: сценарій відпрацював, усі раділи, а завтра той самий незмінений скрипт червоний. Тому канон і каже, що частину даних параметризують не заради достовірності, а щоб тест узагалі виконувався більш ніж один раз. Виробляти такі значення прямо під час прогону не варто: генерація зʼїдає процесор і памʼять тієї самої машини, яка створює навантаження, тож дані готують заздалегідь, а коли унікальність усе-таки потрібна в рантаймі, беруть найдешевшу операцію — склеюють значення з файлу зі сталим параметром прогону. І покладатися тут на після тесту не можна: воно може й не виконатися.
Як роздають дані віртуальним користувачам і які припущення тут не справджуються?
Механіки дві, і обидві мовчазні. Набір даних із файлу в JMeter відкриває файл один раз, а далі кожен потік забирає інший рядок, причому читання відбувається на початку кожної ітерації. SharedArray у k6 працює інакше: дані живуть у памʼяті в одному екземплярі, а віртуальний користувач на кожне звернення отримує копію елемента. Хибних припущень навколо них три. Перше — що порядок передбачуваний: рядки розходяться по потоках у порядку їх виконання, і між ітераціями розклад може змінитися. Друге — що файл на десять тисяч рядків сам собою вбереже від повторів: усталена поведінка на кінці файлу — почати з першого рядка, тож щойно ітерацій стає більше, ніж рядків, прогін ходить по колу тими самими даними (альтернативи — виставити всім змінним значення <EOF> або зупинити потік). Третє — що файл читають усі однаково: режим спільного доступу налаштовується, і файл буває спільним для всього плану, своїм на групу потоків, своїм на потік або спільним за ідентифікатором. Кожне з цих припущень тихо змінює те, скільки різних даних насправді побувало в системі.
Які граблі має SharedArray у k6?
Три, і всі виявляються не одразу. Перша: він доступний лише для читання — покласти туди щось із відповіді системи не вийде за побудовою. Друга: конструюється він виключно в контексті ініціалізації, тобто до того, як зʼявилася перша відповідь сервера; для даних, здобутих із системи, це просто не той інструмент. Третя, найтихіша: економію легко втратити самому — виклики .filter() і .map() на вже готовому масиві, серіалізація масиву цілком або повернення його з функції підготовки зводять вигоду нанівець, бо створюють звичайну копію. Варто знати й межу користі: на наборах приблизно до тисячі рядків виграшу в памʼяті практично немає, а приблизно з десяти тисяч рядків економія памʼяті починає перетворюватися й на економію процесора. Числа в доці подані як ілюстративні, тому цитувати їх як жорсткий поріг не варто — це порядок величини, а не константа.
Чому логін не можна зробити в стадії ініціалізації, і де тоді його місце?
Мова про стадії скрипта k6. Тому що ініціалізації заборонено ходити в мережу, і заборона свідома: відповідь на протокольний запит непередбачувана, а стадія має давати той самий результат від прогону до прогону. Здобуття токена — робота стадії підготовки, у якої повний доступ до протоколу. Далі важлива деталь передавання: віддати можна лише дані, тобто те, що серіалізується в JSON, — функцію чи живий клієнт передати не вийде, — і кожен віртуальний користувач отримує власну свіжу копію цих даних. Практичний наслідок: токен доводиться явно підставляти в заголовок кожного запиту, а не покладатися на «сесію, яка вже є». Gatling малює ту саму межу з іншого боку: хуки до й після прогону існують, але користуватися в них SDK не можна.
Що стається з авторизацією між ітераціями віртуального користувача?
Вона зникає, якщо трималася на куках. Дійшовши до кінця своєї функції, користувач починає спочатку, і цей перезапуск чистить куки, а відкриті TCP-зʼєднання може й розірвати. Тобто улюблена схема «залогінився на першій ітерації й ходжу так весь прогін» без окремих заходів не працює, і механіка тут рівно та сама, що у функціональному тесті, просто помножена на кількість користувачів. Виходів два, і вони не рівноцінні. Якщо логін не є предметом виміру — токен беруть у підготовці й підставляють у заголовки, а сам вхід у профіль не включають. Якщо вхід має бути під навантаженням — його роблять окремою транзакцією сценарію з тією часткою, з якою він трапляється в реальному трафіку; інакше сервіс автентифікації отримає навантаження, якого профіль не передбачав, і час решти кроків стане несумісним із реальністю.
Прогін упав, але жоден віртуальний користувач не стартував. Що це було?
Найімовірніше — підготовка. У k6 в неї власний , типово хвилина, і якщо логін плюс даних у нього не вкладаються, тест помирає ще до першого запиту від віртуальних користувачів. Це окремий клас падінь, який системи не є, і читати його як «система не витримала» — помилка діагностики. Поруч стоїть друга неприємність тієї самої стадії: якщо підготовка завершилася аварійно, прибирання не викликається взагалі, тож сміття невдалого прогону лишається в базі й переїжджає в наступний. Звідси два робочі правила: не тримати очищення даних на одному лише прибиранні і виносити важкий сідінг за межі прогону, щоб він не змагався з таймаутом.
Холодний прогін чи прогрітий — який із них правильний?
Жоден не правильний за замовчуванням, і саме це на співбесіді хочуть почути. Кеш дає на одне питання дві різні відповіді, тому форму виходу на навантаження обирають свідомо й фіксують у звіті поруч із цифрами. Настанови SRE радять пробувати обидва варіанти — і плавну рампу, і різкий сплеск, — саме тому, що через ефекти кешування поступове нарощування може дати інший результат, ніж миттєвий вихід на цільовий рівень. Практичний наслідок: посилання на «той самий сценарій» нічого не доводить, поки не названо, як саме прогін виходив на цільове навантаження. І ще одна половина теми, про яку забувають: якщо сервіс тримає стан або кешує, під навантаженням перевіряють не лише швидкість, а й правильність — саме там вилазять тонкі помилки конкурентності, а кеш, що віддає чужі дані, у метриках часу відповіді виглядає кращим за справний.
Чим погана перевірка «довжина тіла більша за N» у стрес-тесті?
Тим, що вона припускає наявність тіла. Поки система не перевантажена й віддає нормальні відповіді, така перевірка працює й навіть здається розумною. Але предмет стрес-тесту — саме той момент, коли система починає відмовляти, а тоді тіла може не бути взагалі. Перевірка звертається до того, чого немає, і падає сам скрипт: замість зафіксованої відмови системи ви отримуєте помилку інструмента, тобто втрачаєте дані рівно там, де вони найцінніші. Загальне правило для великих прогонів формулюють жорстко: скрипт не має нічого припускати про HTTP-відповідь. Стійкий варіант перевіряє спочатку сам факт відповіді й код, потім тип того, що прийшло, і лише після цього — вміст.
Чому радять уживати якомога менше асертів, і де тут межа?
Бо кожна перевірка коштує ресурсів генератора, а її результат ще й записується окремо: у k6 фіксується підсумок кожного окремого чека і кожної групи, тож при великій їх кількості дока прямо радить подумати про скорочення заради продуктивності. Є й другий, менш очевидний бік — звітність: перевірка, що звіряє значення з очікуваним, при провалі типово пише в лог те, що прийшло насправді, — і на масовій відмові метрики помилок розлазяться на тисячі різних рядків, у яких уже нічого не видно. Але з цього не випливає «перевірок краще не мати»: без них тест дешевший рівно настільки, наскільки брехливіший, бо міряє швидкість сторінки помилки. Робоча межа проста — перевіряти те, що відрізняє успішну бізнес-транзакцію від невдалої, і не перевіряти кожен елемент розмітки.
Асерт не спрацював, хоча потрібний текст на сторінці точно є. Що дивитися першим?
Два усталені налаштування шаблону. По-перше, регістр важливий: «Замовлення» і «замовлення» для асерта — різні рядки. По-друге, шаблон в асерті відповіді за замовчуванням працює в багаторядковому режимі, а це означає, що крапка не збігається з переносом рядка: вираз, який мав «пройти наскрізь» через відформатований HTML чи JSON із відступами, тихо не знаходить нічого. Обидві причини дають однаковий симптом — очима текст видно, асерт червоний. Обидва усталені значення перевизначаються розширеним синтаксисом регулярного виразу, але спершу треба зрозуміти, яке з них спрацювало. Тому діагностика починається з того, щоб один раз подивитися на сире тіло відповіді, а не на сторінку в браузері: саме там видно і фактичний регістр, і те, чи розірваний потрібний фрагмент переносом рядка.
Чому дані називають найважливішою частиною перф-стенда і чому їх не можна «перерахувати коефіцієнтом»?
Бо розмір і структура даних здатні кардинально змінити результат навантажувального тесту — і в переліку того, що на стенді має збігатися з продакшеном, дані стоять раніше за конфігурацію заліза й мережі. Мала вибірка або вибірка іншої складності дає оманливі результати, і особливо помітно це тоді, коли в проді на систему чекає великий набір. Ключова ж деталь — у непередбачуваності: наскільки саме обсяг даних вплине на продуктивність, до реального тесту передбачити важко, тому й коефіцієнта, на який можна помножити, не існує. Структура стоїть тут поруч з обсягом, а не після нього: канон вимагає близькості до продакшенових і за розміром, і за структурою, а мільйон однотипних рядків поводиться інакше, ніж мільйон справжніх — із їхнім розподілом і звʼязками. Звідси й два процесні наслідки — відновлювати початкові дані перед наступним циклом, якщо тест їх змінює, і валідувати середовище контрольним набором тестів із перевіркою журналів і вмісту бази до того, як почнеться справжній прогін.
Прогін переїхав на кілька генераторів. Що першим ламається в кореляції й параметризації?
Дві речі, і обидві виглядають як баг продукту. Перша: у розподіленому прогоні Gatling профіль за замовчуванням розкладають між вузлами, тож крок «залогінитися одним технічним користувачем і роздати токен усім» перестає працювати — він дістанеться котромусь одному вузлу, а решта стартує без токена й видасть лавину відмов авторизації. Друга: у JMeter файли даних клієнт вузлам не розсилає, тож або на кожній машині лежить копія одного й того самого файлу — і тоді унікальність за побудовою миттєво зникає, бо вузли візьмуть ті самі значення, — або набір заздалегідь ділять між файлами вузлів. Обидва симптоми, тобто масові помилки, яких не було на одній машині, читаються як деградація системи, хоча причина цілком у конфігурації прогону. Тому перший розподілений прогін має сенс робити коротким : він перевіряє не систему, а те, що самі генератори налаштовані однаково й дані не перетинаються.
Три ситуації, у яких зелений прогін нічого не доводить: звіт, де падає третій крок, а винен другий; файл на десять тисяч рядків, з якого система побачила шістсот; і набір перевірок, який розсипається саме тоді, коли система почала відмовляти. Скрізь — що дивитися і чому.
Кейс 1. Падає крок 3, а розбирати треба крок 2
Нічний прогін чекауту, 400 . У чаті вже пишуть «кошик не тримає навантаження». Ось зведення :
Транзакція Запитів Помилок p(95)
1. GET /login 4 812 0.0% 128 ms
2. POST /api/session 4 812 0.0% 141 ms
3. GET /api/cart/CART_ID 4 812 98.7% 22 ms
4. POST /api/cart/items 61 0.0% 390 ms
Перше, що впадає в око — не відсоток, а час: 22 мілісекунди на кроці, який упав. Дивимося сирий запит одного з невдалих семплів:
GET /api/cart/CART_NOT_FOUND HTTP/1.1
Authorization: Bearer eyJhbGciOi...
І відповідь кроку 2, з якої ідентифікатор мав узятися:
{
"session": { "token": "eyJhbGciOi...", "cart": { "id": "c-9f31a0" } },
"server_time": "2026-08-02T02:11:07Z"
}
Витяг у скрипті налаштований так:
Екстрактор (крок 2)
Імʼя змінної ......... CART_ID
Вираз ................ "cartId"\s*:\s*"([^"]+)"
Номер збігу .......... 0
Значення за замовч. .. CART_NOT_FOUND
Що дивитися і чому:
22 ms— це підпис помилки, а не швидкості. Успішний запит до кошика коштує сотні мілісекунд: похід у базу, підрахунок, серіалізація. Двадцять дві — це відповідь, зібрана до будь-якої бізнес-логіки, тобто404або400. Коли крок швидший за сусідів у рази, першою гіпотезою має бути не оптимізація, а .- Імʼя поля в JSON —
idусерединіcart, а вираз шукаєcartId. Класична причина, чому кореляція, зроблена «за памʼяттю» чи скопійована з іншого , не збігається ні з чим. Побачити це можна лише в сирій відповіді, тому розбір починається з неї, а не з коду скрипта. - Значення за замовчуванням перетворило поломку на тиху. Вираз не збігся — змінна все одно існує, підстановка все одно відбулася, запит все одно пішов. Механізм корисний: без нього неможливо відрізнити незбіг виразу від невиконаного елемента чи не тієї змінної в запиті. Але побічний ефект саме такий, і читається він у логах миттєво, якщо задавати впізнаване значення на кшталт
CART_NOT_FOUND, а не порожній рядок. - Номер збігу
0— друга бомба, яка тут ще не вибухнула. Нуль у JMeter — це вказівка взяти випадкове з наявних входжень. Якби вираз таки збігався, але в тілі було кілька входжень, сценарій став би недетермінованим: більшість ітерацій зелені, частина падає без видимої причини. Тому номер збігу задають явно навіть за одного очікуваного входження. 98.7%, а не100%— окрема підказка. Кілька десятків ітерацій усе-таки пройшли (звідси й 61 запит на кроці 4). Це або гонка з іншим сценарієм, який створив кошик, або залишки даних попереднього прогону. Розбираючи такі «майже стовідсоткові» відмови, корисно перевірити, чи не змішалися дані двох прогонів у базі.- Правильний фікс — не лише виправити вираз. Поруч ставлять перевірку на сам витяг: якщо змінна дорівнює значенню за замовчуванням, ітерацію позначають невдалою прямо на кроці 2. Тоді наступного разу звіт покаже червоним той крок, у якому справді сталася поломка, і на розбір піде пʼять хвилин, а не ранок.
Кейс 2. Файл на 10 000 рядків, з якого система побачила 600
Двогодинний прогін на 200 віртуальних користувачів, файл із десятьма тисячами облікових записів. Після прогону DBA приносить неприємну статистику: у логах бази — шістсот унікальних user_id, кожен рівно по 200 разів, і половина припала на очікування блокувань. Ось конфігурація, яка це зробила:
Набір даних із файлу
Файл ................. users.csv (10 000 рядків)
Змінні ............... LOGIN,PASSWORD
Перечитувати з початку файлу ..... True
Зупинити потік на EOF ............ False
Режим спільного доступу .......... Поточний потік
Що дивитися і чому:
- Режим «поточний потік» відкриває файл окремо для кожного потоку — і кожен іде від його початку. Двісті потоків рухаються файлом практично в ногу: на першій своїй ітерації беруть перший рядок, на другій — другий. За дві години кожен потік зробив близько 600 ітерацій, тож у роботі побували перші 600 рядків, а решта 9 400 не бралися жодного разу. Дока на цей випадок радить інше: щоб у кожного потоку був власний набір значень, готують набір файлів — по одному на потік, із номером потоку в імені.
- Звідси й блокування. У кожен момент прогону двісті потоків працюють з тим самим обліковим записом — це рівно та конкурентність, від якої застерігає канон: дані заблоковані, поки користувач їх оновлює, і цифри виходять гіршими за реальні. Дзеркальний бік теж можливий: якби набір був маленьким і система його цілком закешувала, цифри вийшли б кращими за реальні. Обидва спотворення дає одна й та сама помилка, тому «ми взяли гірший випадок» тут не аргумент.
- Усталений режим ховає другу пастку — перечитування з початку файлу. Коли файл спільний для всіх потоків, рядки між ними не дублюються, але щойно ітерацій стає більше, ніж рядків, читання мовчки починається з першого рядка й прогін іде тими самими даними по колу. Альтернативи знати варто: виставити всім змінним значення
<EOF>або зупиняти потік на кінці файлу — тоді нестачу даних видно одразу, замість того щоб тихо псувати результат. - Порядок рядків не гарантований. Рядки розходяться по потоках у порядку їх виконання, і між ітераціями розклад може змінитися. Тому будувати логіку на кшталт «перший потік — адмін, другий — менеджер» не можна: ролі розводять окремими групами потоків або окремими файлами.
Той самий набір у k6 виглядає так, і тут інші граблі:
import { SharedArray } from 'k6/data';
import http from 'k6/http';
// Контекст ініціалізації: масив будується один раз на процес, у памʼяті — одна копія.
const users = new SharedArray('users', () => JSON.parse(open('./users.json')));
// Сталий маркер прогону — дешева унікальність за побудовою.
const RUN_ID = __ENV.RUN_ID;
export default function () {
const u = users[(__VU - 1) % users.length];
// Ніякої генерації випадкових значень у рантаймі: тільки конкатенація.
const orderName = `${RUN_ID}-${u.orderSeed}`;
http.post(`${__ENV.BASE_URL}/api/orders`, JSON.stringify({
login: u.login,
name: orderName,
}), { headers: { 'Content-Type': 'application/json' } });
}
Що дивитися і чому:
SharedArrayживе лише в контексті ініціалізації і лише для читання. Покласти туди щось із відповіді системи неможливо за побудовою — це інструмент для підготовлених даних, а не для кореляції.- Економію легко втратити власними руками. Виклики
.filter()або.map()на вже готовому масиві, серіалізація його цілком чи повернення з функції підготовки створюють звичайну копію на кожного віртуального користувача — і сенс конструкції зникає. Фільтрувати треба всередині функції, що будує масив. - На маленьких наборах вигоди немає взагалі. Приблизно до тисячі рядків виграш у памʼяті непомітний, а приблизно з десяти тисяч рядків економія памʼяті починає переходити і в економію процесора. Числа в доці подані як ілюстративні, тож це орієнтир, а не поріг.
RUN_IDвирішує проблему другого прогону. Назва замовлення мусить бути унікальною, і без такого маркера скрипт упаде, щойно спробує створити запис із назвою, яка вже є в базі. Генерувати унікальність випадковими числами дорожче: процесор і памʼять витрачаються на машині навантаження, тобто безпосередньо зменшують кількість користувачів, яких вона тягне.- На кількох генераторах цей код ще не готовий. Розкласти набір по вузлах — ручна робота: у JMeter, наприклад, дока прямо каже, що клієнт не розсилає вузлам файли даних. Тож або кожен вузол отримує свій шматок набору, або всі вони візьмуть ті самі рядки — і унікальність за побудовою зникне разом із достовірністю.
Кейс 3. Перевірки, які не розсипаються, коли система починає падати
Стрес-тест на пік. Задача набору перевірок — не «переконатися, що все добре», а зафіксувати момент, коли стало погано. Ось два варіанти того самого чека:
import http from 'k6/http';
import { check } from 'k6';
export default function () {
const res = http.get(`${__ENV.BASE_URL}/orders/last`);
// Крихко: припускає, що тіло є завжди. Під відмовою тіла може не бути,
// і впаде сам скрипт — замість того щоб записати відмову системи.
check(res, {
'сторінка не порожня': (r) => r.body.length > 1000,
});
// Стійко: спершу факт відповіді й код, потім тип, аж тоді вміст.
check(res, {
'статус 200': (r) => r.status === 200,
'тіло прийшло рядком': (r) => typeof r.body === 'string',
'у тілі є номер замовлення': (r) =>
typeof r.body === 'string' && r.body.includes('Замовлення №'),
});
}
Що з чого складається у звіті — і чим це коштує:
| Перевірка | Що ловить | Чим коштує |
|---|---|---|
| Код відповіді | Явні відмови й редіректи на сторінку помилки | Майже нічого |
| Вміст (підрядок) | 200, у якому насправді текст помилки | Дешевше за регулярний вираз |
| Регулярний вираз на тілі | Складні випадки без сталого обрамлення | Помітно дорожче на кожному запиті |
| Асерт тривалості | Повільні відповіді, які формально успішні | Змінює читання звіту: такі відповіді стають помилками |
Що дивитися і чому:
- Порядок умов у стійкому варіанті — не косметика. Спочатку перевіряється те, що не може зникнути (код), потім тип отриманого, і лише наприкінці вміст. У великому прогоні діє жорсткіше правило: скрипт не має нічого припускати про HTTP-відповідь, бо коли сервер відмовляє, тіла може не бути взагалі.
- Помилка в скрипті — гірша за помилку системи. Впав чек — і замість зафіксованої відмови ви маєте зіпсовану ітерацію: дані втрачені саме в тій точці прогону, заради якої тест і робили.
- Неявний
existsстворює хибне відчуття . Якщо крок валідації не задано явно, перевірка підтверджує лише те, що щось знайшлося, а не що значення правильне. Тобто «чек є, і він зелений» ще не означає, що ви перевіряєте те, що думаєте. - тривалості переносить повільні відповіді в помилки. Кожна відповідь, довша за задану кількість мілісекунд, позначається невдалою — це зручно для гейта, але звіт після цього читається інакше: починає включати не тільки відмови. Про це домовляються заздалегідь, інакше на розборі виникає суперечка «то в нас 3% помилок чи 0.1%».
- Асерт не спрацював, хоча текст точно є — дивіться на два усталені налаштування. Регістр важливий, а шаблон в асерті відповіді за замовчуванням працює в багаторядковому режимі, тобто крапка не збігається з переносом рядка. Обидві причини дають однаковий симптом і однаково швидко перевіряються на сирому тілі.
- Кардинальність метрик помилок — тиха ціна ретельності. Більшість перевірок з умовою на очікуване значення при провалі логують фактичне значення; на масовій відмові це роздуває звіт на тисячі різних рядків, у яких уже нічого не видно. Тому асертів радять якнайменше, а кожен чек у k6 ще й записується окремо. Межа проста: перевіряти те, що відрізняє успішну бізнес-транзакцію від невдалої, і не перевіряти кожен елемент розмітки — бо прогін без перевірок дешевший рівно настільки, наскільки брехливіший.
Кореляція
- Можу дати означення через механізм — динамічні ідентифікатори записаного сеансу замінюються змінними, які скрипт бере з попередніх відповідей, — одразу зняти омонім зі статистикою й додати головне твердження ISTQB: брак кореляції або крива кореляція є основною причиною, чому записаний скрипт не відтворюється.
- Знаю різницю «кореляція vs параметризація» за джерелом значення: перше приходить із відповіді системи, друге — зі списку, готового до прогону; формально канон уважає кореляцію частиною параметризації, і цю деталь варто назвати самому.
- Можу пояснити, чому провалена кореляція валить наступний запит, а не свій власний: витяг і — один із шести кроків (тип, витяг, перетворення, валідація, імʼя, збереження), а збереження працює лише при успішній перевірці, тож змінна лишається порожньою або зі старим значенням. І не приймаю «інструмент усе скорелював сам» на віру: автоматика закриває прості випадки, складніші потребують ручної кореляції або нових правил.
Витяг значень і його ціна
- Задаю номер збігу явно, бо усталені різні: нуль у JMeter означає випадковий вибір збігу, а в Gatling без явного кроку витягу спрацьовує неявний
findі бере перше входження — звідси сценарій, який «інколи падає». - Розумію подвійну природу значення за замовчуванням: воно існує, щоб відрізнити незбіг від невиконаного елемента чи не тієї змінної, і воно ж ховає поломку: підстановка відбулася, крок виконався, а система у відповідь пише про помилку.
- Знаю три механізми витягу й критерій вибору: з обовʼязковою групою захоплення (крім випадку
$0$) — коли обрамлення нестале; ліва й права межі — коли стале; JSONPath або JMESPath — для API, причому в JSONPath немає належної специфікації і реалізації розходяться, а JMESPath має граматику й тести відповідності. Памʼятаю і про ціну: підрядок дешевший за регулярний вираз для перевірки наявності, а окремі режими роботи з тілом відповіді в JMeter помітно бʼють по продуктивності генератора.
Параметризація й роздача даних
- Можу назвати обидва напрямки спотворення від одного набору — повне дає цифри значно кращі за прод, конкуренція за ті самі записи значно гірші, тож разом це не консервативна оцінка, а недостовірна — і додати, що числа «скільки значень достатньо» канон не називає: критерій якісний і залежить від системи, даних та вимог тесту.
- Розумію дані, унікальні за побудовою: без параметризації тест зелений рівно один раз, а генерувати такі значення в рантаймі дорого — дані готують заздалегідь, у крайньому разі склеюючи рядок із файлу зі сталим параметром прогону.
- Знаю семантику набору даних із файлу — файл відкривається один раз, кожен потік бере інший рядок, читання на початку кожної ітерації — і три припущення, які тут не справджуються: порядок рядків не гарантований, на кінці файлу типово починається перечитування з першого рядка (альтернативи —
<EOF>у змінних або зупинка потоку), а режим спільного доступу визначає, скільки різних даних реально побувало в системі. - Знаю обмеження
SharedArrayу k6: лише для читання, будується виключно на стадії ініціалізації, тобто нічого зі здобутого від сервера туди не покладеш, а.filter(),.map()чи серіалізація масиву цілком зводять економію нанівець; приблизно до тисячі рядків вигоди в памʼяті майже немає, а приблизно з десяти тисяч економія памʼяті починає переходити і в економію процесора.
Авторизація і стан прогону
- Можу пояснити, чому в стадії ініціалізації немає HTTP — заборона тримає стадію відтворюваною, бо відповідь на протокольний запит непередбачувана, — і що логін по токен тому живе в підготовці, яка віддає лише дані у форматі JSON, кожному користувачеві власною копією.
- Памʼятаю, що на межі ітерацій користувач скидається — куки чистяться, зʼєднання можуть рватися, тож «залогінився один раз і ходжу весь прогін» без окремих заходів не працює, — і знаю два падіння, які системи не є: підготовка не вклалася у власний (типово 60 секунд) та аварійна підготовка, після якої не викликається й дані невдалого прогону лишаються в системі.
- Тримаю логін окремою з тією часткою, з якою він трапляється в реальному трафіку, і звіряю строк дії токена з тривалістю прогону: коли токени протухають гуртом, графік не відрізнити від справжньої деградації.
Кеш і валідація відповідей
- Розумію, що прогрітий прогін на малому наборі — не оптимістична оцінка, а вимір іншої системи; форму виходу на навантаження варто пробувати обидві (плавну рампу й різкий сплеск) і називати у звіті, бо ефекти кешування дають різні результати. І памʼятаю другу половину теми: для сервісів зі станом під навантаженням перевіряють правильність, а не лише швидкість, — кеш, що віддає чужі дані, у метриках виглядає кращим за справний.
- Знаю мінімум перевірки перф-сценарію (код відповіді плюс вміст), пастку неявного
exists— без явного кроку валідації підтверджується лише те, що щось знайшлося, — і чому перевірка на довжину тіла крихка: вона припускає наявність тіла, тож при відмові системи ламається сам скрипт. - Знаю три речі, які змінюють не код, а звіт: асерт тривалості переводить повільну відповідь у розряд помилок; чутливість до регістру й багаторядковий режим шаблону дають «асерт не спрацював, хоча текст є»; а звичка логувати фактичне значення при провалі на масовій відмові розносить метрики помилок на тисячі різних рядків.
- Тримаю межу балансу: асертів радять якнайменше й кожен чек записується окремо, але прогін без перевірок дешевший рівно настільки, наскільки брехливіший — лишаю ті перевірки, за якими видно, вдалася бізнес-операція чи ні, і не покриваю кожен шматок розмітки.
Реалістичність даних і розподілений прогін
- Памʼятаю, що дані стоять першими в переліку частин перф-стенда — раніше за конфігурацію заліза й мережі, — структура важить не менше за розмір, а «перерахувати коефіцієнтом» не працює, бо вплив обсягу даних до реального тесту передбачити важко.
- Знаю два процесні наслідки: відновлення початкових даних перед наступним циклом, якщо тест їх змінює, і валідацію середовища контрольним набором тестів із перевіркою журналів та вмісту бази.
- Знаю, що ламається при переїзді на кілька генераторів: у розподіленому прогоні Gatling профіль за замовчуванням розкладають між вузлами, тож крок «один користувач бере токен» дістанеться лише одному з них, а в JMeter клієнт не розсилає вузлам файли даних — набір ділять між ними руками задля унікальності.
Квіз
Перед стартом
- Питань: 17
- Поріг «зараховано»: ≥70% правильних відповідей.
- Результат впливає на прогрес; завалені питання підуть у чергу повторення.
- Квіз впливає на компліт теми: тема стає «пройдено», лише коли прочитано теорію І квіз складено на ≥70%.
Питання
Які значення в записаному скрипті обовʼязково доводиться робити змінними?