vyvchy
    Теми розділу

    10 · Performance testing

    Кореляція, параметризація і тестові дані

    Зміст

    Записали сценарій, поставили 200 — і всі ітерації зелені, з часом відповіді 40 мілісекунд. Виглядає як чудова продуктивність. Насправді система з другого запиту віддає «сесія недійсна», а ви щойно зміряли, як швидко вона генерує сторінку помилки. Випадок не екзотичний: канон ISTQB називає неправильну кореляцію або її відсутність основною причиною, чому записані скрипти не відтворюються.

    Ця глава — про чотири речі між «скрипт запускається» і «цифрам можна вірити»: динамічні значення з відповідей, свої дані в кожного віртуального користувача, стан прогону (авторизація й кеш) і перевірки, які роблять зміряний час осмисленим. Механіку конкретного інструмента тут не розбираємо: інструменти — у сусідній главі, стенд і генератори — у наступній.

    Кореляція: значення, яких у скрипті бути не може

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

    Чому без неї нічого не працює: записаний трафік містить значення конкретної сесії — ідентифікатор кошика, токен форми, номер замовлення. При повторі сервер бачить ідентифікатор, якого вже не існує, і відповідає помилкою. Просунуті інструменти можуть робити частину кореляції автоматично, тож у простих випадках вона буває непомітною, але в складніших потрібна ручна кореляція або нові правила кореляції — тобто «інструмент усе зробив» перевіряють, а не припускають.

    Механіка кореляції та перевірки — одна: перевірки роблять дві речі — верифікують, що відповідь відповідає очікуванням, і захоплюють елементи з відповіді. той самий, із шести кроків: задати тип перевірки, витягти, перетворити, валідувати, назвати, зберегти. Кореляція — це він з увімкненим останнім кроком.

    Три пастки витягу, які нічого не кажуть уголос

    1. Номер збігу. У JMeter нуль означає, що інструмент вибере збіг випадково, додатне NN-й збіг. У Gatling усталений перший: якщо крок витягу не визначено явно, виконується неявний find, що бере перше або єдине можливе входження. Різні усталені значення в різних інструментах — готове джерело сценарію, що «інколи падає».
    2. Значення за замовчуванням. У JMeter якщо не збігся, змінна отримує значення за замовчуванням, і дока пояснює навіщо: без нього важко відрізнити, чи вираз не збігся, чи елемент узагалі не спрацював, чи використано не ту змінну. Зворотний бік того самого механізму — поломка стає тихою.
    3. Збереження лише при успіху. У Gatling збереження діє тільки якщо перевірка успішна — тобто збіглася з відповіддю й пройшла валідацію. Звідси головний симптом теми: провалена кореляція ламає наступний запит, а не той, у якому сталася.

    Так

    Ні

    Так

    Ні

    Відповідь на запит N

    Витяг знайшов значення?

    Валідація пройшла?

    Підставлено значення
    за замовчуванням

    Значення збережено у змінну

    Збереження не відбувається:
    у змінній старе або порожнє

    Запит N+1 іде з чужим значенням

    Падає запит N+1
    Причину шукати в N

    Так

    Ні

    Так

    Ні

    Відповідь на запит N

    Витяг знайшов значення?

    Валідація пройшла?

    Підставлено значення
    за замовчуванням

    Значення збережено у змінну

    Збереження не відбувається:
    у змінній старе або порожнє

    Запит N+1 іде з чужим значенням

    Падає запит N+1
    Причину шукати в N

    Чим витягати

    МеханізмКоли доречний
    Регулярний вираз — застосовує 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 в них не можна.

    Підготовка: один вхід, токен отримано

    Копія даних кожному VU

    Ітерація 1: запити з токеном

    Кінець ітерації:
    куки очищено

    Ітерація 2: кук уже немає,
    токен із копії лишився

    Підготовка: один вхід, токен отримано

    Копія даних кожному VU

    Ітерація 1: запити з токеном

    Кінець ітерації:
    куки очищено

    Ітерація 2: кук уже немає,
    токен із копії лишився

    Наша практика (не канон). Далі — те, як це роблять команди, з якими ми працювали; окремого стандарту чи специфікації під це немає. Вхід у систему тримають окремою транзакцією: якщо кожна ітерація починається з логіна, ви навантажуєте сервіс автентифікації сильніше, ніж передбачає профіль, і цифри решти кроків стають несумісними з реальністю. Токени намагаються отримати заздалегідь — у підготовці або поза прогоном, готовим пулом облікових записів, — а логін додають у профіль у тій частці, у якій він трапляється в реальних даних. І окремо перевіряють, що час життя токена перекриває прогін: масове протухання токенів у графіках виглядає точно як деградація системи.

    Кеш: холодний прогін проти прогрітого

    Кеш дає на одне питання дві різні відповіді, і жодна з них не є правильною за замовчуванням. Механізм канон називає в темі даних: якщо тестовий набір малий, дані можуть бути повністю закешовані — скопійовані з диска в памʼять для швидшого доступу, — і результати будуть значно кращими, ніж у продакшені, де ті самі дані читалися б із диска. Прогрітий прогін на маленькому наборі — не «оптимізм», а вимір іншої системи.

    Тому вибір між холодним і прогрітим прогоном варто робити свідомо й називати у звіті. Канон SRE радить пробувати обидві форми виходу на навантаження: і плавну рампу, і різкий сплеск, бо через ефекти кешування поступове нарощування може дати інші результати, ніж миттєвий вихід на очікуваний рівень. Це найкоротше пояснення, чому «ми ж міряли той самий сценарій» аргументом не є.

    Друга половина теми — коректність, а не швидкість. Якщо ви навантажуєте сервіс зі станом або з кешуванням, тест має відстежувати стан між взаємодіями й перевіряти правильність під високим навантаженням — саме там зазвичай проявляються тонкі помилки конкурентності. Кеш, який під навантаженням віддає чужі дані, у метриках виглядає краще за справний. Механіка самого HTTP-кешування — тема глави про кешування.

    Валідація відповідей: без перевірок ви міряєте швидкість помилки

    Перф-тест без перевірки вмісту міряє швидкість сторінки помилки — і робить це переконливо, бо помилки віддаються швидко й стабільно. Звичний мінімум перевірок перф-сценарію — код відповіді плюс вміст, і тонкість у другій половині: якщо крок валідації не визначено явно, виконується неявний exists, тобто «щось знайшлося», а не «значення правильне».

    Найважливіше твердження теми стосується не , а моменту, коли тест став цікавим: у великих стрес-тестах скрипт не повинен нічого припускати про HTTP-відповідь. Приклад — перевірка довжини тіла: вона працює, поки система не перевантажена й повертає нормальні відповіді, але коли система починає падати, працює не так, як очікується, бо припускає, що тіло є завжди, — а якщо сервер відмовляє, тіла може не бути. Результат — помилка в самому скрипті замість зафіксованої відмови системи, тобто дані втрачаються там, де вони найцінніші.

    // крихка перевірка: припускає, що тіло є завжди
    const okFragile = body.length > 1000;
    
    // стійка: спершу факт відповіді, потім її вміст
    const okResilient = status === 200 && typeof body === 'string' && body.includes('Замовлення №');

    Три речі змінюють не код, а звіт. тривалості перевіряє, що кожна відповідь надійшла в межах заданого часу, і будь-яка відповідь, довша за задану кількість мілісекунд, позначається як невдала — повільна відповідь потрапляє в , а не лише в хвіст розподілу. За замовчуванням шаблон в асерті відповіді працює в багаторядковому режимі, тобто метасимвол . не збігається з переносом рядка, і регістр теж важливий — дві найчастіші причини «асерт не спрацював, хоча текст на екрані є». І кардинальність: більшість перевірок з умовою на очікуване значення за замовчуванням логують фактичне значення при провалі, що може дати дуже високу кардинальність метрик помилок — на масовій відмові звіт розсипається на тисячі рядків.

    Звідси компроміс, який не люблять називати вголос. Настанови JMeter радять уживати якомога менше асертів, а k6 попереджає, що результат кожної окремої перевірки й групи записується окремо, тож за великої їх кількості варто подумати про видалення заради продуктивності. Це не «перевірки шкідливі»: без перевірок тест дешевший і брехливіший. Межа — перевіряти те, що відрізняє успішну транзакцію від невдалої, а не кожен елемент сторінки. Частка помилок і пороги — теми метрик та вимог до продуктивності.

    Реалістичність даних: тест на порожній базі бреше

    Коли перелічують, що на стенді мусить збігатися з продакшеном, дані називають першими — раніше за конфігурацію заліза й мережі. І одразу пояснюють, чому це не дрібниця: розмір і структура даних можуть кардинально вплинути на результати навантажувального тесту, а використання малої вибірки або вибірки іншої складності може дати оманливі результати, особливо коли продакшн працюватиме з великим набором даних. І головне: передбачити, наскільки обсяг даних впливає на продуктивність, до реального тестування важко. Тобто «у нас тисяча замовлень замість мільйона, але пропорцію ми врахуємо» не працює — враховувати нема з чого. Правило просте: чим ближчі до продакшенових за розміром і структурою, тим надійніші результати, причому структура важить не менше за розмір — мільйон однакових записів не є тим самим мільйоном, що в проді.

    Два процесні наслідки легко пропустити. Перший: якщо під час тесту дані генеруються або змінюються, може знадобитися відновити початкові дані перед наступним циклом — інакше другий прогін міряє вже іншу базу, і порівняння з базовою лінією втрачає сенс. Другий: перед виконанням тестів середовище й налаштування мають бути валідовані — контрольованим набором тестів із перевіркою їх результату й роботи засобів моніторингу, а щоб переконатися, що тест працює як задумано, використовують аналіз журналів і перевірку вмісту бази. Решта складників достовірності стенда — у главі про середовище й генератори.

    Типові помилки

    «Інструмент усе скорелював сам». Виглядає як зручність, а насправді автоматика покриває прості випадки — у складніших потрібна ручна кореляція. Ознака — сценарій, який відтворюється «залежно від дня».

    «Тест зелений, значить кореляція працює». Виглядає як доказ, а насправді підставлене значення за замовчуванням робить поломку тихою: змінна є, запит іде, сервер відповідає помилкою.

    «Даних вистачить — у файлі десять тисяч рядків». Виглядає як запас, а насправді на кінці файлу типово вмикається перечитування з першого рядка, і прогін іде по колу тими самими користувачами.

    «Додамо перевірок на кожен елемент — надійніше». Виглядає як ретельність, а насправді кожна перевірка записується окремо й коштує ресурсів генератора, а логування фактичних значень на масовій відмові роздуває кардинальність метрик помилок.

    «На стенді швидше, ніж на проді, — запас є». Виглядає як хороша новина, а насправді малий набір даних міг поміститися в памʼять, тоді як прод читає з диска. Це не запас, а інший вимір.

    Підсумок

    1. Кореляція — це значення з відповіді, параметризація — значення зі списку. Перше лікує невідтворюваність скрипта, друге — недостовірність результатів.
    2. Зламана кореляція валить наступний запит, а не той, у якому сталася, а значення за замовчуванням робить поломку тихою.
    3. Один користувач на всіх спотворює результати в обидва боки: кеш зробить цифри кращими за реальні, блокування — гіршими. Це не консервативна оцінка, а недостовірна.
    4. Без перевірки вмісту тест міряє швидкість помилки, а перевірка під happy path ламається саме тоді, коли система почала падати.
    5. Дані стоять першими серед частин стенда, і вплив їхнього обсягу важко передбачити до реального тесту.

    Можливі питання

    «Що таке кореляція в навантажувальному тестуванні?» Інтервʼюер слухає, чи не почнете ви говорити про статистичний звʼязок величин. Сильна відповідь — означення через механізм плюс наслідок: без кореляції записаний скрипт не відтворюється.

    «Скрипт падає на другому кроці. Що робите?» Перевіряють хід думки: подивитися відповідь попереднього запиту, знайти динамічне значення, перевірити номер збігу й значення за замовчуванням — і лише потім думати про систему.

    «Навіщо параметризувати дані, якщо тест і так проходить?» Дивляться, чи бачите ви обидва напрямки спотворення — кешування й конкурентність — і чи згадаєте дані, унікальні за побудовою: без них тест зелений рівно один раз.

    «Як переконатися, що ви міряєте саме успішну операцію?» Слухають про перевірку коду відповіді разом із вмістом і про стійкість перевірки до відсутнього тіла; згадка про ціну перевірок читається як досвід.

    «Стенд показує кращі цифри, ніж прод. Чому це не привід радіти?» Малий набір даних міг поміститися в памʼять, стан кешів залежить від форми виходу на навантаження, а вплив обсягу даних до реального тесту передбачити важко. Правильний фінал — назвати відмінності стенда, а не множити результат на коефіцієнт.

    Джерела

    Кореляція: значення, яких у скрипті бути не може

    • 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 — розподіл профілю між вузлами й наслідок для сценарію з одним ініціалізаційним користувачем.

    Параметризація: у кожного віртуального користувача свої дані

    Авторизація в перф-сценаріях

    • 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) — дані першими в переліку найважливіших частин тестового середовища, вплив розміру й структури даних, непередбачуваність цього впливу до тестування, відновлення початкових даних між циклами, валідація середовища контрольним набором тестів.

    Пояснення

    «Поясни» працює з власним API-ключем Anthropic: запит іде з вашого браузера прямо до Anthropic.

    Свій API-ключ (BYOK)

    Вставте власний ключ Anthropic — пояснення працюватиме на реальній моделі. Ключ зберігається лише у вашому браузері (localStorage), нікуди не надсилається, крім api.anthropic.com, і не логується.