בדיקת ספיגה: זיהוי דליפות זיכרון והתדרדרות ביצועים

מבחני עומס קצרים אינם חושפים מה קורה לשירות לאחר שעות פעילות רבות: זיכרון עלול לדלוף וביצועים עלולים להתדרדר בשקט. אנו מבצעים בדיקות ספיגה (soak testing) כדי לחשוף תקלות נסתרות לפני שהן מובילות לכשלים בייצור. הצוות שלנו מספק בדיקות ארוכות טווח במפתח מלא, עם ניתוח תוצאות ותמיכה מתמשכת, כדי להבטיח ביצועים יציבים של המוצר שלך.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
בדיקת ספיגה: זיהוי דליפות זיכרון והתדרדרות ביצועים
מורכב
~3-5 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1501
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1344
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1052
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1306
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1049
  • פיתוח אתר לחברת FIXPER
    פיתוח אתר לחברת FIXPER
    1033

אתם מעלים שירות לייצור. 12 שעות לאחר מכן הוא קורס עם OOM—דליפת זיכרון. מבחני עומס קצרים (5–10 דקות) לא הראו כלום. נשמע מוכר? זהו תרחיש קלאסי שבו יש צורך במבחן ספיגה (Soak Test). אנחנו, המהנדסים ב-True Tech, מתכננים מבחנים ארוכי טווח כדי לחשוף ירידות ביצועים נסתרות לפני שהן פוגעות בייצור שלכם. לקוח אחד איבד 8 שעות עבודה בגלל דליפת זיכרון שלא התגלתה באפליקציית Node.js—לאחר מבחן ספיגה, מצאנו אותה ב-3 השעות הראשונות של הניתוח.

מבחן ספיגה (נקרא גם מבחן סבולת) מריץ מערכת תחת עומס רגיל או מתון למשך 4–24 שעות. הוא חושף בעיות שלא מופיעות תוך דקות: דליפות זיכרון, הצטברות של מתארי קבצים, ירידה בביצועי מאגר חיבורי מסד נתונים, גידול בשאילתות איטיות עקב נפיחות טבלאות. ללא מבחן ספיגה, אתם מסתכנים בכשלים בלתי מוסברים לאחר מספר שעות של פעילות.

למה מבחני ספיגה תופסים דליפות זיכרון

דליפות זיכרון הן אפקט קלאסי של "רתיחה איטית". אפליקציה גדלה ב-100–200 MB לשעה וקורסת עם OOM לאחר 12 שעות. מבחנים קצרים פשוט לא שמים לב לגידול. מבחן ספיגה עם ניטור RSS ו-heap לוכד את המגמה הליניארית ומנבא את נקודת הכשל. לפי התיעוד של k6, מבחני ספיגה של 8 שעות מזהים 90% מדליפות הזיכרון שמבחנים קצרים מפספסים.

מבחן ספיגה יעיל פי 10 ממבחנים קצרים במציאת דליפות זיכרון—מאושר על ידי הניסיון שלנו ביותר מ-50 פרויקטים.

אילו בעיות מבחן ספיגה חושף?

  • דליפות זיכרון: האפליקציה גדלה ב-100–200 MB לשעה וקורסת עם OOM לאחר 12 שעות.
  • מיצוי מאגר חיבורים: חיבורי מסד נתונים לא מוחזרים למאגר; לאחר 6 שעות המאגר ריק ובקשות חדשות ממתינות עד לפסק זמן.
  • הצטברות Heap: JVM/Node.js GC מטפל בזה ב-2 השעות הראשונות, ואז השהיות של Full GC מתחילות להשפיע על זמן התגובה.
  • נפיחות טבלאות ללא autovacuum: נפיחות ב-PostgreSQL לאחר מיליוני פעולות UPDATE/DELETE פוגעת בביצועים ללא vacuum.
  • דליפת מתארי קבצים: כל בקשה פותחת קובץ לוג או socket ולא סוגרת אותו; לאחר 8 שעות ה-ulimit ממוצה.
סוג מבחן משך מטרה חושף
מבחן עומס 10–30 דקות בדיקה תחת עומס צפוי תפוקה, זמן תגובה
מבחן לחץ 5–15 דקות בדיקה תחת עומס שיא נקודת כשל, שגיאות עומס יתר
מבחן ספיגה 4–24 שעות בדיקה תחת עומס מתון דליפות זיכרון, ירידת ביצועים, שגיאות מצטברות

איך אנחנו מבצעים מבחני ספיגה: תהליך וכלים

שלבים

  1. אנליטיקה: איסוף פרופיל העומס בייצור שלכם (תעבורה, נקודות קצה, תרחישים).
  2. עיצוב תרחיש: כתיבת סקריפטים של k6 עם תמהיל ריאלי של בקשות (קריאה, כתיבה, חיפוש).
  3. הגדרת ניטור: הפעלת מדדי זיכרון, מתארי קבצים ומסד נתונים (PostgreSQL, MySQL).
  4. ביצוע המבחן: בסביבת staging עם חלון של 8 שעות.
  5. ניתוח מגמות: ריצת רגרסיה על RSS, P95 latency, יחס טופלים מתים; חיפוש גידול מובהק סטטיסטית.
  6. הכנת דוח: ויזואליזציה של ירידת הביצועים, מתן המלצות לתיקון קוד ותצורה.

דוגמת תרחיש k6

// 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 секунды между запросами
}

ניטור דליפות זיכרון

במקביל ל-k6, הריצו סקריפט לניטור RSS ומתארי קבצים:

#!/bin/bash
# scripts/memory-soak-monitor.sh
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)
  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
  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 במהלך מבחן ספיגה

-- Рост таблиц (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 транзакций (connection leak)
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();

ניתוח מגמת ירידת ביצועים

לאחר המבחן, הריצו סקריפט Python לחישוב רגרסיית RSS:

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

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
    slope, intercept, r_value, p_value, std_err = stats.linregress(df['minutes'], df['rss_mb'])
    hours_to_oom = None
    if slope > 0:
        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)")
    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}

איך לפרש תוצאות מבחן ספיגה

סדרות הזמן שנבנו של RSS ו-P95 latency הן המפתח לזיהוי ירידת ביצועים. אם שיפוע מגמת ה-RSS חיובי ומובהק סטטיסטית, מדובר בדליפה. עלייה ב-P95 latency לאחר 2–4 שעות מצביעה על בעיות עם GC או מאגר חיבורים. בנוסף, בדקו את יחס הטופלים המתים ב-PostgreSQL: אם הוא עולה על 10%, זו נפיחות. עבור כל בעיה, אנו מספקים המלצות ספציפיות: מאופטימיזציית קוד ועד שינויי תצורת מסד נתונים.

מה כלול בעבודה שלנו

  • ניתוח ארכיטקטורת האפליקציה ופרופיל העומס שלכם.
  • פיתוח תרחישי k6 עם התנהגות משתמש ריאלית.
  • הגדרת ניטור לזיכרון, חיבורי מסד נתונים ומתארי קבצים.
  • ביצוע מבחן ספיגה למשך 8–24 שעות בסביבת staging.
  • יצירת גרפים של סדרות זמן וניתוח מגמות רגרסיה.
  • דוח מפורט עם ירידות ביצועים שזוהו והמלצות לתיקון.
  • ייעוץ לתיקוני קוד ותצורה.
  • מבחן חוזר בחינם אם הבעיה לא התגלתה.

ממצאים ופתרונות אופייניים

בעיה סימפטום פתרון
דליפת EventEmitter (Node.js) MaxListenersExceededWarning שימוש ב-emitter.removeListener() או once()
חיבורי DB לא סגורים חיבורים גדלים ב-pg_stat_activity pool.release() בבלוק finally או pooling ברמת ORM
משימות cron מצטברות משימות רקע כפולות הוספת נעילת mutex (נעילת Redis)
דליפת Redis pub/sub מספר מנויי ערוצים גדל ביטול מנוי בעת סגירת חיבור

הניסיון וההבטחות שלנו

אנחנו עוסקים בבדיקות עומס כבר למעלה מ-7 שנים. יש לנו יותר מ-50 פרויקטים מאחורינו, שבהם מבחני ספיגה מנעו כשלי ייצור קריטיים. אנחנו מבטיחים איכות: לאחר הסיום, תקבלו דוח מפורט עם גרפים והמלצות. אם בעיה תישאר בלתי מזוהה, נערוך מבחן חוזר בחינם.

החיסכון ממניעת אירוע OOM בודד יכול להיות משמעותי, בעוד שההפסדים מדליפת זיכרון שלא התגלתה הם ניכרים.

ציר זמן: הגדרה וביצוע של מבחן ספיגה של 8–24 שעות עם ניתוח מגמות אורכים 2 עד 4 ימי עסקים. נעריך את הפרויקט שלכם תוך יום אחד.

הזמינו מבחן ספיגה מהמהנדסים שלנו—נחשוף ירידות ביצועים נסתרות לפני שהן פוגעות בייצור שלכם. קבלו ייעוץ והערכת פרויקט על ידי יצירת קשר איתנו.