QA Interview Hub · База знань · Тести
System Design
Масштабування й надійність
Вертикальне і горизонтальне масштабування
Вертикальне масштабування - більша машина: більше CPU, пам'яті. Просто, не треба міняти код, але є стеля і одна точка відмови.
Горизонтальне масштабування - більше екземплярів за load balancer. Майже без стелі і стійкіше до відмов, але вимагає stateless сервісів, спільного сховища для стану і ускладнює систему.
Autoscaling додає екземпляри за метрикою: CPU, кількість запитів, довжина черги. Важливі нюанси: новий екземпляр стартує не миттєво, а БД часто масштабується гірше, ніж сервіси.
Що тестує QA в контексті масштабування:
- Load test - поведінка під очікуваним навантаженням: латентність p95 і p99, частка помилок.
- Stress test - де система ламається і як: деградує поступово чи падає повністю.
- Spike test - різкий пік, наприклад розпродаж або виплата зарплат о 9:00. Чи встигає autoscaling.
- Soak test - кілька годин навантаження, щоб знайти витоки пам'яті і з'єднань.
Інструменти: k6, Gatling, JMeter, Locust. Вибір інструменту менш важливий, ніж реалістичний профіль навантаження і середовище, близьке до проду.
Типова помилка: проганяти навантаження на staging з порожньою БД. Запити, які швидкі на 1000 рядках, стають повільними на 50 мільйонах. Потрібен реалістичний обсяг даних.
На співбесіді: Горизонтальне масштабування вимагає stateless сервісів і дає стійкість, вертикальне простіше, але має стелю. Як QA я перевіряю масштабування load, stress, spike і soak тестами з реалістичним обсягом даних і дивлюся на p95, p99 і частку помилок.
Ідемпотентність, retries і rate limiting
Ідемпотентність - повторний виклик операції дає той самий результат, що й перший. GET, PUT, DELETE за специфікацією HTTP ідемпотентні, POST - ні. Для платежів роблять ідемпотентним і POST через idempotency key: клієнт генерує унікальний ключ і передає в заголовку. Сервер запам'ятовує результат за ключем і на повтор повертає той самий результат, не створюючи новий платіж.
Навіщо: мережа ненадійна. Клієнт відправив переказ, відповідь загубилася, клієнт повторив. Без ключа - подвійне списання.
Тест-кейси для idempotency key:
- Повтор з тим самим ключем і тим самим тілом - той самий переказ, одне списання.
- Той самий ключ з іншим тілом - помилка, а не тихе ігнорування.
- Два паралельні запити з одним ключем - один переказ.
- Повтор після закінчення строку зберігання ключа - поведінка за специфікацією.
Retries з exponential backoff і jitter. Клієнт повторює запит із зростаючою паузою (1, 2, 4 секунди) і випадковим зсувом, щоб тисячі клієнтів не повторювали одночасно. Повторювати можна тільки ідемпотентні операції.
Rate limiting обмежує кількість запитів від клієнта за час, наприклад 100 запитів за хвилину. Перевищення - відповідь 429 Too Many Requests, часто із заголовком Retry-After. Алгоритми: token bucket, sliding window. Тестувати: межу ліміту, скидання вікна, ліміти для різних клієнтів незалежні, клієнт правильно реагує на 429.
На співбесіді: Для платежів я обов'язково тестую idempotency key: повтор, паралельні запити з одним ключем і той самий ключ з іншим тілом. Окремо перевіряю rate limiting на межі ліміту і що клієнт коректно обробляє 429 з backoff, а не повторює запит миттєво.
CAP і надійність на базовому рівні
CAP-теорема каже: при мережевому розділенні (P, partition) розподілена система має вибрати між консистентністю (C) і доступністю (A). Розділення в реальній мережі трапляються, тому вибір фактично між C і A в момент збою.
- CP: краще відмовити в операції, ніж віддати неузгоджені дані. Приклад: списання з рахунку. Краще помилка, ніж подвійне списання.
- AP: краще відповісти, навіть якщо дані трохи застарілі. Приклад: каталог товарів, кількість лайків, рекомендації.
Для QA практичний висновок: треба знати, яку поведінку обрано для кожної функції, і тестувати саме її. Якщо баланс - CP, то при недоступності ledger API має повернути помилку, а не старий баланс з кешу.
Патерни надійності, які варто знати:
- Timeout на кожен виклик іншого сервісу. Без таймауту один повільний сервіс блокує всі потоки.
- Circuit breaker: після серії помилок перестає викликати залежність на час і одразу повертає fallback або помилку.
- Graceful degradation: якщо сервіс рекомендацій недоступний, сторінка оплати все одно працює.
- Bulkhead: ізоляція ресурсів, щоб збій однієї функції не забрав усі з'єднання.
Як це тестувати: chaos testing і fault injection. Затримати відповіді залежності, вбити екземпляр, обірвати з'єднання з БД - і перевірити, що система поводиться так, як задумано. Починати варто зі staging і з контрольованих експериментів.
На співбесіді: CAP на практиці означає вибір між консистентністю і доступністю під час мережевого збою: для балансу я очікую CP, для каталогу допустимо AP. Я тестую цю поведінку через fault injection: затримки, падіння залежностей, і перевіряю таймаути, circuit breaker і деградацію некритичних функцій.