Реалізація Soak Testing (тривале тестування під навантаженням)

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

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

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація Soak Testing (тривале тестування під навантаженням)
Складна
~3-5 робочих днів
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Soak Testing: тестування під тривалим навантаженням

Soak test (endurance test) — запуск системи під нормальним або помірним навантаженням протягом 4–24 годин. Виявляє проблеми, які не проявляються за хвилини: витік пам'яті, накопичення файлових дескрипторів, деградація пула соединень БД, зростання повільних запитів через накопичення даних.

Що виявляють soak тести

Витік пам'яті: програма зростає на 100–200MB/годину та падає з OOM через 12 годин.

Вичерпання пула соединень: соединення з БД не повертаються в пул, через 6 годин пул вичерпаний — нові запити чекають до timeout.

Накопичення у heap: JVM/Node.js GC впоратися перші 2 години, потім Full GC паузи впливають на latency.

Зростання таблиць без autovacuum: PostgreSQL bloat — після мільйона операцій UPDATE/DELETE продуктивність деградує без vacuum.

Витік файлових дескрипторів: кожен запит відкриває лог-файл або сокет без закриття — через 8 годин ulimit вичерпаний.

k6 сценарій soak тесту

// tests/soak/endurance.js
import http from 'k6/http'
import { check, sleep } from 'k6'
import { Rate, Trend, Gauge } from 'k6/metrics'

const errorRate = new Rate('errors')
const p95Latency = new Trend('p95_latency_trend', true)
const activeUsers = new Gauge('active_users')

export const options = {
  stages: [
    { duration: '5m',  target: 50 },   // розігрів
    { duration: '8h',  target: 50 },   // 8 годин нормального навантаження
    { duration: '5m',  target: 0 },    // охолодження
  ],

  thresholds: {
    // Latency не повинна деградувати під час тесту
    http_req_duration: ['p(95)<600'],

    // Помилок не повинно бути (витік проявляється через помилки)
    errors: ['rate<0.001'],

    // Час підключення до БД не повинен зростати
    http_req_connecting: ['p(95)<50'],
  }
}

const BASE_URL = __ENV.BASE_URL || 'https://staging.example.com'

export function setup() {
  const res = http.post(`${BASE_URL}/api/auth/login`, JSON.stringify({
    email: '[email protected]',
    password: __ENV.TEST_PASSWORD
  }), { headers: { 'Content-Type': 'application/json' } })

  return { token: res.json('token') }
}

export default function(data) {
  const headers = {
    'Authorization': `Bearer ${data.token}`,
    'Content-Type': 'application/json'
  }

  activeUsers.add(1)

  // Мікс операцій, типових для реального трафіку
  const scenario = Math.random()

  if (scenario < 0.6) {
    // 60%: читання даних
    const r = http.get(`${BASE_URL}/api/products?page=${Math.ceil(Math.random() * 50)}`,
      { headers })
    check(r, { 'read: 200': (r) => r.status === 200 })
    errorRate.add(r.status !== 200)

  } else if (scenario < 0.8) {
    // 20%: запис даних (створюємо реальні записи)
    const r = http.post(`${BASE_URL}/api/cart/items`, JSON.stringify({
      productId: Math.ceil(Math.random() * 1000),
      quantity: 1
    }), { headers })
    check(r, { 'write: 2xx': (r) => r.status < 300 })
    errorRate.add(r.status >= 400)

  } else if (scenario < 0.9) {
    // 10%: пошук
    const r = http.get(`${BASE_URL}/api/search?q=test&limit=20`, { headers })
    check(r, { 'search: 200': (r) => r.status === 200 })
    errorRate.add(r.status !== 200)

  } else {
    // 10%: профіль користувача
    const r = http.get(`${BASE_URL}/api/me`, { headers })
    check(r, { 'profile: 200': (r) => r.status === 200 })
    errorRate.add(r.status !== 200)
  }

  // Додати p95 для часового ряду
  p95Latency.add(http.get(`${BASE_URL}/api/health`).timings.duration)

  sleep(Math.random() * 2 + 0.5)  // 0,5–2,5 секунди між запитами
}

Моніторинг витік пам'яті

#!/bin/bash
# scripts/memory-soak-monitor.sh
# Запускати паралельно з k6 soak тестом

APP_PID=$(pgrep -f "node server.js")
LOG_FILE="soak-memory-$(date +%Y%m%d-%H%M).csv"

echo "timestamp,rss_mb,heap_used_mb,heap_total_mb,external_mb,fd_count" > $LOG_FILE

while true; do
  TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)

  # Node.js пам'ять через endpoint /metrics (якщо expose)
  METRICS=$(curl -s http://localhost:3000/metrics/memory)
  RSS=$(echo $METRICS | jq -r '.rss')
  HEAP_USED=$(echo $METRICS | jq -r '.heapUsed')
  HEAP_TOTAL=$(echo $METRICS | jq -r '.heapTotal')
  EXTERNAL=$(echo $METRICS | jq -r '.external')

  # Файлові дескриптори
  FD_COUNT=$(ls /proc/$APP_PID/fd 2>/dev/null | wc -l)

  echo "$TS,$RSS,$HEAP_USED,$HEAP_TOTAL,$EXTERNAL,$FD_COUNT" >> $LOG_FILE
  echo "[$TS] RSS: ${RSS}MB | Heap: ${HEAP_USED}/${HEAP_TOTAL}MB | FDs: $FD_COUNT"

  sleep 60  # кожну хвилину
done
// Express/Fastify endpoint для експонування пам'яті
app.get('/metrics/memory', (req, res) => {
  const mem = process.memoryUsage()
  res.json({
    rss: Math.round(mem.rss / 1024 / 1024),
    heapUsed: Math.round(mem.heapUsed / 1024 / 1024),
    heapTotal: Math.round(mem.heapTotal / 1024 / 1024),
    external: Math.round(mem.external / 1024 / 1024),
  })
})

Моніторинг PostgreSQL під час soak

-- Запускати кожні 15 хвилин та зберігати результати

-- Зростання таблиць (bloat)
SELECT relname, n_live_tup, n_dead_tup,
       round(n_dead_tup::numeric / nullif(n_live_tup + n_dead_tup, 0) * 100, 1) AS dead_pct,
       last_vacuum, last_autovacuum
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC LIMIT 10;

-- Накопичення idle транзакцій (витік соединень)
SELECT count(*), state, wait_event_type
FROM pg_stat_activity
WHERE pid != pg_backend_pid()
GROUP BY state, wait_event_type
ORDER BY count DESC;

-- Зростання розміру тимчасових файлів
SELECT temp_files, temp_bytes
FROM pg_stat_database
WHERE datname = current_database();

Аналіз тренду деградації

# analyze_soak.py
import pandas as pd
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt

def analyze_memory_trend(csv_file: str):
    df = pd.read_csv(csv_file, parse_dates=['timestamp'])
    df['minutes'] = (df['timestamp'] - df['timestamp'].iloc[0]).dt.total_seconds() / 60

    # Лінійна регресія для RSS
    slope, intercept, r_value, p_value, std_err = stats.linregress(
        df['minutes'], df['rss_mb']
    )

    hours_to_oom = None
    if slope > 0:
        # При якому споживанні пам'яті почнеться OOM (припускаємо 4GB ліміт)
        oom_threshold = 4096
        current_rss = df['rss_mb'].iloc[-1]
        hours_to_oom = (oom_threshold - current_rss) / (slope * 60)

    print(f"Memory growth rate: {slope:.2f} MB/min ({slope*60:.1f} MB/hour)")
    print(f"R²: {r_value**2:.3f} (1.0 = perfect linear growth = definite leak)")

    if hours_to_oom:
        print(f"Estimated OOM in: {hours_to_oom:.1f} hours")

    # Тест на статистичну значимість зростання
    if p_value < 0.01 and slope > 0.1:
        print("⚠️  MEMORY LEAK DETECTED (statistically significant growth)")
    else:
        print("✓  No significant memory leak detected")

    return {
        'slope_mb_per_min': slope,
        'r_squared': r_value ** 2,
        'hours_to_oom': hours_to_oom,
        'leak_detected': p_value < 0.01 and slope > 0.1
    }

# Запуск
result = analyze_memory_trend('soak-memory-20240315-100000.csv')

Типові знахідки та рішення

Витік EventEmitter (Node.js): MaxListenersExceededWarning у логах. Додати emitter.removeListener() або використовувати once().

Незакриті DB соединення: використовувати pool.release() у finally блоці або ORM-level connection pooling.

Накопичення cron jobs: якщо cron запускається поки попередній ще виконується — додати mutex lock.

Redis pub/sub витік: відписатися від каналів при завершенні соединення.

Часовий графік

Налаштування та запуск soak тесту на 8–24 години з аналізом трендів пам'яті та продуктивності — 2–3 робочих дні.