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 і деградацію некритичних функцій.