Przejdź do głównej zawartości

Analiza wydajności

Wydajność to różnica między oprogramowaniem, które zachwyca, a oprogramowaniem, które frustruje. Czy optymalizujesz czasy odpowiedzi, redukujesz zużycie pamięci, czy skalujesz dla milionów użytkowników, Claude Code przekształca optymalizację wydajności z zgadywania w inżynierię opartą na danych. Ta lekcja bada, jak wykorzystać pomoc sztucznej inteligencji do kompleksowej analizy i optymalizacji wydajności.

Scenariusz: Twoja aplikacja była szybka z 100 użytkownikami. Teraz z 10 000 użytkowników raczkuje. Zapytania do bazy danych się przedawniają, API odpowiadają wolno, a użytkownicy narzekają. Tradycyjne podejście: rzuć więcej serwerów i miej nadzieję. Z Claude Code: systematyczna analiza, która znajduje i naprawia prawdziwe wąskie gardła.

Tydzień 1: Zaczynają się skargi
- "Aplikacja jest wolna"
- Dodaj więcej serwerów
- Nadal wolna
Tydzień 2: Losowa optymalizacja
- Buforuj wszystko
- Dodaj indeksy wszędzie
- Zepsuć coś innego
Tydzień 3: Próba profilowania
- Złożona konfiguracja profilera
- Przytłaczające dane
- Niepewność co naprawić
Tydzień 4: Konsultanci
- Kosztowna analiza
- Ogólne rekomendacje
- Marginalne poprawy
> Skonfiguruj kompleksowe profilowanie wydajności dla naszej aplikacji:
> - Profilowanie CPU
> - Profilowanie pamięci
> - Analiza zapytań do bazy danych
> - Pomiar czasu żądań sieciowych
> - Wydajność renderowania
> Utwórz dashboard wizualizacji

Claude tworzy kompletną konfigurację profilowania:

  1. Monitorowanie wydajności aplikacji

    performance-monitor.js
    const { performance, PerformanceObserver } = require('perf_hooks');
    const v8 = require('v8');
    const os = require('os');
    class PerformanceMonitor {
    constructor() {
    this.metrics = {
    requests: new Map(),
    database: new Map(),
    cache: new Map(),
    cpu: [],
    memory: []
    };
    this.startMonitoring();
    }
    startMonitoring() {
    // Pomiar czasu żądań
    this.requestObserver = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
    if (entry.name.startsWith('request-')) {
    this.recordRequest(entry);
    }
    }
    });
    this.requestObserver.observe({ entryTypes: ['measure'] });
    // Metryki systemowe
    this.systemInterval = setInterval(() => {
    this.recordSystemMetrics();
    }, 1000);
    // Statystyki sterty V8
    this.heapInterval = setInterval(() => {
    this.recordHeapStatistics();
    }, 5000);
    }
    recordRequest(entry) {
    const route = entry.name.replace('request-', '');
    if (!this.metrics.requests.has(route)) {
    this.metrics.requests.set(route, {
    count: 0,
    totalTime: 0,
    minTime: Infinity,
    maxTime: 0,
    p50: 0,
    p95: 0,
    p99: 0,
    times: []
    });
    }
    const stats = this.metrics.requests.get(route);
    stats.count++;
    stats.totalTime += entry.duration;
    stats.minTime = Math.min(stats.minTime, entry.duration);
    stats.maxTime = Math.max(stats.maxTime, entry.duration);
    stats.times.push(entry.duration);
    // Oblicz percentyle
    if (stats.times.length > 100) {
    const sorted = [...stats.times].sort((a, b) => a - b);
    stats.p50 = sorted[Math.floor(sorted.length * 0.5)];
    stats.p95 = sorted[Math.floor(sorted.length * 0.95)];
    stats.p99 = sorted[Math.floor(sorted.length * 0.99)];
    // Zachowaj tylko ostatnie 1000 próbek
    stats.times = stats.times.slice(-1000);
    }
    }
    recordSystemMetrics() {
    const cpus = os.cpus();
    const totalIdle = cpus.reduce((acc, cpu) => acc + cpu.times.idle, 0);
    const totalTick = cpus.reduce((acc, cpu) =>
    acc + Object.values(cpu.times).reduce((a, b) => a + b), 0);
    const cpuUsage = 100 - ~~(100 * totalIdle / totalTick);
    this.metrics.cpu.push({
    timestamp: Date.now(),
    usage: cpuUsage,
    loadAvg: os.loadavg()
    });
    // Wykorzystanie pamięci
    const memUsage = process.memoryUsage();
    this.metrics.memory.push({
    timestamp: Date.now(),
    rss: memUsage.rss,
    heapTotal: memUsage.heapTotal,
    heapUsed: memUsage.heapUsed,
    external: memUsage.external,
    arrayBuffers: memUsage.arrayBuffers
    });
    // Zachowaj tylko dane z ostatniej godziny
    const hourAgo = Date.now() - 3600000;
    this.metrics.cpu = this.metrics.cpu.filter(m => m.timestamp > hourAgo);
    this.metrics.memory = this.metrics.memory.filter(m => m.timestamp > hourAgo);
    }
    recordHeapStatistics() {
    const heapStats = v8.getHeapStatistics();
    const heapSpaces = v8.getHeapSpaceStatistics();
    // Wykryj wycieki pamięci
    if (heapStats.used_heap_size > heapStats.heap_size_limit * 0.9) {
    console.warn('OSTRZEŻENIE: Wykorzystanie sterty powyżej 90%');
    this.takeHeapSnapshot();
    }
    }
    async takeHeapSnapshot() {
    const { writeHeapSnapshot } = require('v8');
    const filename = `heap-${Date.now()}.heapsnapshot`;
    writeHeapSnapshot(filename);
    console.log(`Zrzut sterty zapisany do ${filename}`);
    }
    getReport() {
    const report = {
    timestamp: new Date().toISOString(),
    requests: {},
    system: {
    cpu: this.calculateStats(this.metrics.cpu.map(m => m.usage)),
    memory: this.calculateStats(this.metrics.memory.map(m => m.heapUsed))
    }
    };
    // Konwertuj metryki żądań do formatu raportu
    for (const [route, stats] of this.metrics.requests) {
    report.requests[route] = {
    count: stats.count,
    avgTime: stats.totalTime / stats.count,
    minTime: stats.minTime,
    maxTime: stats.maxTime,
    p50: stats.p50,
    p95: stats.p95,
    p99: stats.p99
    };
    }
    return report;
    }
    calculateStats(values) {
    if (values.length === 0) return {};
    const sorted = [...values].sort((a, b) => a - b);
    return {
    min: sorted[0],
    max: sorted[sorted.length - 1],
    avg: values.reduce((a, b) => a + b) / values.length,
    p50: sorted[Math.floor(sorted.length * 0.5)],
    p95: sorted[Math.floor(sorted.length * 0.95)],
    p99: sorted[Math.floor(sorted.length * 0.99)]
    };
    }
    }
    // Middleware Express do pomiaru czasu żądań
    function performanceMiddleware(monitor) {
    return (req, res, next) => {
    const start = performance.now();
    const route = req.route?.path || req.path;
    res.on('finish', () => {
    const duration = performance.now() - start;
    performance.measure(`request-${route}`, {
    start,
    duration
    });
    });
    next();
    };
    }
    module.exports = { PerformanceMonitor, performanceMiddleware };
  2. Profilowanie zapytań do bazy danych

    db-profiler.js
    class DatabaseProfiler {
    constructor(db) {
    this.db = db;
    this.queries = new Map();
    this.slowQueryThreshold = 100; // ms
    this.wrapDatabase();
    }
    wrapDatabase() {
    const originalQuery = this.db.query.bind(this.db);
    this.db.query = async (sql, params) => {
    const start = performance.now();
    const stack = new Error().stack;
    try {
    const result = await originalQuery(sql, params);
    const duration = performance.now() - start;
    this.recordQuery(sql, duration, true, stack);
    if (duration > this.slowQueryThreshold) {
    this.handleSlowQuery(sql, duration, params);
    }
    return result;
    } catch (error) {
    const duration = performance.now() - start;
    this.recordQuery(sql, duration, false, stack);
    throw error;
    }
    };
    }
    recordQuery(sql, duration, success, stack) {
    const queryKey = this.normalizeQuery(sql);
    if (!this.queries.has(queryKey)) {
    this.queries.set(queryKey, {
    query: queryKey,
    count: 0,
    totalTime: 0,
    minTime: Infinity,
    maxTime: 0,
    failures: 0,
    locations: new Set()
    });
    }
    const stats = this.queries.get(queryKey);
    stats.count++;
    stats.totalTime += duration;
    stats.minTime = Math.min(stats.minTime, duration);
    stats.maxTime = Math.max(stats.maxTime, duration);
    if (!success) stats.failures++;
    // Wyodrębnij lokalizację wywołania
    const caller = this.extractCaller(stack);
    if (caller) stats.locations.add(caller);
    }
    normalizeQuery(sql) {
    // Usuń konkretne wartości, aby pogrupować podobne zapytania
    return sql
    .replace(/\b\d+\b/g, '?')
    .replace(/'[^']*'/g, '?')
    .replace(/\s+/g, ' ')
    .trim();
    }
    extractCaller(stack) {
    const lines = stack.split('\n');
    // Znajdź pierwszą linię nienależącą do frameworka
    for (const line of lines.slice(2)) {
    if (!line.includes('node_modules') && line.includes('.js')) {
    const match = line.match(/at .* \((.*:\d+:\d+)\)/);
    return match ? match[1] : null;
    }
    }
    return null;
    }
    async handleSlowQuery(sql, duration, params) {
    console.warn(`Wykryto wolne zapytanie (${duration.toFixed(2)}ms):`);
    console.warn(sql);
    // Uzyskaj plan wykonania zapytania
    try {
    const explainResult = await this.db.query(`EXPLAIN ANALYZE ${sql}`, params);
    console.warn('Plan wykonania:', explainResult.rows);
    // Sprawdź brakujące indeksy
    const indexSuggestions = this.suggestIndexes(explainResult.rows);
    if (indexSuggestions.length > 0) {
    console.warn('Sugerowane indeksy:', indexSuggestions);
    }
    } catch (error) {
    // EXPLAIN może nie działać dla niektórych zapytań
    }
    }
    suggestIndexes(explainRows) {
    const suggestions = [];
    for (const row of explainRows) {
    const plan = row['QUERY PLAN'] || row.plan || '';
    // Szukaj skanów sekwencyjnych na dużych tabelach
    if (plan.includes('Seq Scan') && plan.includes('rows=')) {
    const rowMatch = plan.match(/rows=(\d+)/);
    if (rowMatch && parseInt(rowMatch[1]) > 1000) {
    const tableMatch = plan.match(/on (\w+)/);
    if (tableMatch) {
    suggestions.push(`Rozważ indeks na ${tableMatch[1]}`);
    }
    }
    }
    // Szukaj kosztownych sortowań
    if (plan.includes('Sort') && plan.includes('Sort Method: external')) {
    suggestions.push('Rozważ indeks dla kolumn ORDER BY');
    }
    }
    return [...new Set(suggestions)];
    }
    getSlowQueries(limit = 10) {
    const queries = Array.from(this.queries.values())
    .filter(q => q.maxTime > this.slowQueryThreshold)
    .sort((a, b) => b.totalTime - a.totalTime)
    .slice(0, limit);
    return queries.map(q => ({
    query: q.query,
    count: q.count,
    avgTime: q.totalTime / q.count,
    maxTime: q.maxTime,
    totalTime: q.totalTime,
    locations: Array.from(q.locations)
    }));
    }
    }
    module.exports = DatabaseProfiler;
  3. Wykrywanie wycieków pamięci

    memory-leak-detector.js
    const { writeHeapSnapshot } = require('v8');
    const fs = require('fs').promises;
    class MemoryLeakDetector {
    constructor(options = {}) {
    this.threshold = options.threshold || 100 * 1024 * 1024; // 100MB
    this.checkInterval = options.checkInterval || 60000; // 1 minuta
    this.samples = [];
    this.leaks = [];
    this.startMonitoring();
    }
    startMonitoring() {
    this.interval = setInterval(() => {
    this.checkMemory();
    }, this.checkInterval);
    // Ustaw bazę początkową
    this.takeBaseline();
    }
    async takeBaseline() {
    if (global.gc) {
    global.gc();
    }
    await new Promise(resolve => setTimeout(resolve, 1000));
    const baseline = process.memoryUsage();
    this.samples.push({
    timestamp: Date.now(),
    ...baseline
    });
    }
    checkMemory() {
    const current = process.memoryUsage();
    const sample = {
    timestamp: Date.now(),
    ...current
    };
    this.samples.push(sample);
    // Zachowaj tylko próbki z ostatniej godziny
    const hourAgo = Date.now() - 3600000;
    this.samples = this.samples.filter(s => s.timestamp > hourAgo);
    // Analizuj w poszukiwaniu wycieków
    const leak = this.detectLeak();
    if (leak) {
    this.handleLeak(leak);
    }
    }
    detectLeak() {
    if (this.samples.length < 5) return null;
    // Oblicz tempo wzrostu
    const recent = this.samples.slice(-5);
    const old = this.samples.slice(0, 5);
    const recentAvg = recent.reduce((sum, s) => sum + s.heapUsed, 0) / recent.length;
    const oldAvg = old.reduce((sum, s) => sum + s.heapUsed, 0) / old.length;
    const growth = recentAvg - oldAvg;
    const growthRate = growth / oldAvg;
    // Wykryj ciągły wzrost
    if (growth > this.threshold && growthRate > 0.2) {
    return {
    growth,
    growthRate,
    currentHeap: recentAvg,
    samples: this.samples.slice(-10)
    };
    }
    return null;
    }
    async handleLeak(leak) {
    console.error('Wykryto wyciek pamięci!', {
    growth: `${(leak.growth / 1024 / 1024).toFixed(2)} MB`,
    rate: `${(leak.growthRate * 100).toFixed(2)}%`,
    currentHeap: `${(leak.currentHeap / 1024 / 1024).toFixed(2)} MB`
    });
    this.leaks.push({
    timestamp: Date.now(),
    ...leak
    });
    // Wykonaj zrzut sterty
    await this.captureHeapSnapshot();
    // Analizuj źródła alokacji
    const sources = await this.findAllocationSources();
    if (sources.length > 0) {
    console.error('Potencjalne źródła wycieków:', sources);
    }
    }
    async captureHeapSnapshot() {
    const filename = `leak-${Date.now()}.heapsnapshot`;
    writeHeapSnapshot(filename);
    console.log(`Zrzut sterty zapisany: ${filename}`);
    // Porównaj z poprzednim zrzutem jeśli istnieje
    if (this.previousSnapshot) {
    await this.compareSnapshots(this.previousSnapshot, filename);
    }
    this.previousSnapshot = filename;
    }
    async findAllocationSources() {
    // Sprawdź typowe źródła wycieków
    const sources = [];
    // Nasłuchiwacze zdarzeń
    if (process._events) {
    const eventCounts = {};
    for (const event in process._events) {
    const listeners = process._events[event];
    const count = Array.isArray(listeners) ? listeners.length : 1;
    if (count > 10) {
    sources.push(`Wysoka liczba nasłuchiwaczy dla '${event}': ${count}`);
    }
    }
    }
    // Zmienne globalne
    const globalVars = Object.keys(global).length;
    if (globalVars > 100) {
    sources.push(`Wysoka liczba zmiennych globalnych: ${globalVars}`);
    }
    // Timery
    const activeTimers = process._getActiveHandles
    ? process._getActiveHandles().filter(h => h.constructor.name === 'Timer').length
    : 0;
    if (activeTimers > 50) {
    sources.push(`Wysoka liczba aktywnych timerów: ${activeTimers}`);
    }
    return sources;
    }
    }
    module.exports = MemoryLeakDetector;
> Utwórz profilowanie wydajności frontendu:
> - Czasy renderowania komponentów React
> - Analiza rozmiaru bundla
> - Waterfall sieci
> - Monitorowanie Core Web Vitals
frontend-profiler.js
class FrontendProfiler {
constructor() {
this.metrics = {
components: new Map(),
vitals: [],
resources: []
};
this.initializeObservers();
}
initializeObservers() {
// Performance Observer dla różnych metryk
if ('PerformanceObserver' in window) {
// Largest Contentful Paint
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
this.metrics.vitals.push({
name: 'LCP',
value: entry.renderTime || entry.loadTime,
timestamp: Date.now()
});
}
}).observe({ entryTypes: ['largest-contentful-paint'] });
// First Input Delay
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
this.metrics.vitals.push({
name: 'FID',
value: entry.processingStart - entry.startTime,
timestamp: Date.now()
});
}
}).observe({ entryTypes: ['first-input'] });
// Cumulative Layout Shift
let clsValue = 0;
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
}
}
this.metrics.vitals.push({
name: 'CLS',
value: clsValue,
timestamp: Date.now()
});
}).observe({ entryTypes: ['layout-shift'] });
}
}
// Profilowanie komponentów React
profileComponent(Component) {
const WrappedComponent = React.forwardRef((props, ref) => {
const renderStart = performance.now();
React.useEffect(() => {
const renderEnd = performance.now();
const componentName = Component.displayName || Component.name || 'Unknown';
if (!this.metrics.components.has(componentName)) {
this.metrics.components.set(componentName, {
renders: 0,
totalTime: 0,
avgTime: 0,
maxTime: 0
});
}
const stats = this.metrics.components.get(componentName);
stats.renders++;
stats.totalTime += renderEnd - renderStart;
stats.avgTime = stats.totalTime / stats.renders;
stats.maxTime = Math.max(stats.maxTime, renderEnd - renderStart);
});
return <Component ref={ref} {...props} />;
});
WrappedComponent.displayName = `Profiled(${Component.displayName || Component.name})`;
return WrappedComponent;
}
// Analiza rozmiaru bundla
async analyzeBundleSize() {
const resources = performance.getEntriesByType('resource');
const scripts = resources.filter(r => r.name.endsWith('.js'));
const analysis = {
totalSize: 0,
scripts: [],
largestScripts: []
};
for (const script of scripts) {
const size = script.transferSize || script.encodedBodySize || 0;
analysis.totalSize += size;
const scriptInfo = {
name: script.name.split('/').pop(),
size,
duration: script.duration,
compressed: script.transferSize < script.decodedBodySize
};
analysis.scripts.push(scriptInfo);
}
analysis.largestScripts = analysis.scripts
.sort((a, b) => b.size - a.size)
.slice(0, 5);
return analysis;
}
// Waterfall sieci
getNetworkWaterfall() {
const resources = performance.getEntriesByType('resource');
return resources
.map(resource => ({
name: resource.name,
type: this.getResourceType(resource.name),
startTime: resource.startTime,
duration: resource.duration,
size: resource.transferSize || 0,
timeline: {
dns: resource.domainLookupEnd - resource.domainLookupStart,
tcp: resource.connectEnd - resource.connectStart,
ssl: resource.secureConnectionStart > 0
? resource.connectEnd - resource.secureConnectionStart
: 0,
ttfb: resource.responseStart - resource.requestStart,
download: resource.responseEnd - resource.responseStart
}
}))
.sort((a, b) => a.startTime - b.startTime);
}
getResourceType(url) {
if (url.match(/\.(js|mjs)$/)) return 'script';
if (url.match(/\.css$/)) return 'stylesheet';
if (url.match(/\.(jpg|jpeg|png|gif|webp|svg)$/)) return 'image';
if (url.match(/\.(woff|woff2|ttf|eot)$/)) return 'font';
if (url.match(/\.json$/)) return 'json';
return 'other';
}
// Generuj raport wydajności
generateReport() {
return {
timestamp: Date.now(),
vitals: this.getWebVitals(),
components: this.getSlowComponents(),
bundle: this.analyzeBundleSize(),
network: this.getNetworkWaterfall()
};
}
getWebVitals() {
const vitals = {};
['LCP', 'FID', 'CLS'].forEach(metric => {
const values = this.metrics.vitals
.filter(v => v.name === metric)
.map(v => v.value);
if (values.length > 0) {
vitals[metric] = {
value: values[values.length - 1],
rating: this.getVitalRating(metric, values[values.length - 1])
};
}
});
return vitals;
}
getVitalRating(metric, value) {
const thresholds = {
LCP: { good: 2500, poor: 4000 },
FID: { good: 100, poor: 300 },
CLS: { good: 0.1, poor: 0.25 }
};
const threshold = thresholds[metric];
if (value <= threshold.good) return 'good';
if (value <= threshold.poor) return 'needs-improvement';
return 'poor';
}
getSlowComponents() {
return Array.from(this.metrics.components.entries())
.map(([name, stats]) => ({ name, ...stats }))
.filter(c => c.avgTime > 16) // Wolniejsze niż 60fps
.sort((a, b) => b.avgTime - a.avgTime)
.slice(0, 10);
}
}
> Przeanalizuj wąskie gardła wydajności naszej aplikacji:
> - Zidentyfikuj wolne zapytania do bazy danych
> - Znajdź operacje intensywnie wykorzystujące CPU
> - Wykryj wycieki pamięci
> - Przeanalizuj opóźnienia sieciowe
> - Profiluj wydajność renderowania
> Dostarcz konkretne rekomendacje optymalizacji
bottleneck-analyzer.js
class BottleneckAnalyzer {
constructor(performanceData) {
this.data = performanceData;
this.bottlenecks = [];
}
async analyze() {
// Analizuj różne aspekty
await Promise.all([
this.analyzeDatabaseQueries(),
this.analyzeCPUUsage(),
this.analyzeMemoryUsage(),
this.analyzeNetworkLatency(),
this.analyzeRenderingPerformance()
]);
// Priorytetyzuj wąskie gardła według wpływu
this.bottlenecks.sort((a, b) => b.impact - a.impact);
return this.generateReport();
}
async analyzeDatabaseQueries() {
const slowQueries = this.data.database.queries
.filter(q => q.avgTime > 100) // > 100ms średnio
.sort((a, b) => b.totalTime - a.totalTime);
for (const query of slowQueries.slice(0, 5)) {
const impact = (query.totalTime / this.data.totalTime) * 100;
this.bottlenecks.push({
type: 'database',
severity: impact > 20 ? 'critical' : impact > 10 ? 'high' : 'medium',
impact,
details: {
query: query.query,
avgTime: query.avgTime,
count: query.count,
totalTime: query.totalTime
},
recommendations: await this.getDatabaseRecommendations(query)
});
}
}
async getDatabaseRecommendations(query) {
const recommendations = [];
// Sprawdź brakujące indeksy
if (query.query.includes('WHERE') && query.avgTime > 200) {
recommendations.push({
title: 'Dodaj indeks',
description: 'Rozważ dodanie indeksu na kolumnach klauzuli WHERE',
example: this.generateIndexSuggestion(query.query)
});
}
// Sprawdź zapytania N+1
if (query.count > 100 && query.query.includes('SELECT')) {
recommendations.push({
title: 'Zapytania wsadowe',
description: 'To zapytanie jest wykonywane wiele razy. Rozważ wsadowe przetwarzanie lub użycie JOIN',
example: 'Użyj pojedynczego zapytania z JOIN lub klauzulą IN zamiast wielu zapytań'
});
}
// Sprawdź pełne skanowanie tabeli
if (query.plan && query.plan.includes('Seq Scan')) {
recommendations.push({
title: 'Unikaj pełnego skanowania tabeli',
description: 'Zapytanie wykonuje skanowanie sekwencyjne',
example: 'Dodaj odpowiednie indeksy lub ogranicz zestaw wyników'
});
}
return recommendations;
}
generateIndexSuggestion(query) {
// Wyodrębnij nazwy tabel i kolumn z klauzuli WHERE
const whereMatch = query.match(/FROM\s+(\w+).*WHERE\s+(\w+)/i);
if (whereMatch) {
return `CREATE INDEX idx_${whereMatch[1]}_${whereMatch[2]} ON ${whereMatch[1]}(${whereMatch[2]});`;
}
return 'CREATE INDEX idx_table_column ON table(column);';
}
async analyzeCPUUsage() {
const cpuSpikes = this.data.cpu.samples.filter(s => s.usage > 80);
if (cpuSpikes.length > 0) {
const avgHighCPU = cpuSpikes.reduce((sum, s) => sum + s.usage, 0) / cpuSpikes.length;
this.bottlenecks.push({
type: 'cpu',
severity: avgHighCPU > 90 ? 'critical' : 'high',
impact: cpuSpikes.length / this.data.cpu.samples.length * 100,
details: {
avgHighCPU,
spikeCount: cpuSpikes.length,
duration: cpuSpikes.length * this.data.cpu.sampleInterval
},
recommendations: [
{
title: 'Profiluj wykorzystanie CPU',
description: 'Użyj profilera CPU do identyfikacji gorących funkcji',
example: 'node --prof app.js && node --prof-process isolate-*.log'
},
{
title: 'Optymalizuj algorytmy',
description: 'Przejrzyj złożoność algorytmiczną w gorących ścieżkach',
example: 'Zastąp operacje O(n²) alternatywami O(n log n)'
},
{
title: 'Dodaj buforowanie',
description: 'Buforuj wyniki obliczeń aby uniknąć powtarzających się kalkulacji',
example: 'Zaimplementuj memoizację dla kosztownych funkcji'
}
]
});
}
}
async analyzeMemoryUsage() {
const memoryGrowth = this.calculateMemoryGrowth();
if (memoryGrowth.rate > 0.1) { // 10% wzrost
this.bottlenecks.push({
type: 'memory',
severity: memoryGrowth.rate > 0.5 ? 'critical' : 'high',
impact: memoryGrowth.rate * 100,
details: {
growthRate: memoryGrowth.rate,
totalGrowth: memoryGrowth.total,
suspectedLeaks: memoryGrowth.leaks
},
recommendations: [
{
title: 'Napraw wycieki pamięci',
description: 'Usuń nasłuchiwacze zdarzeń i wyczyść referencje',
example: `
// Usuń nasłuchiwacze po zakończeniu
emitter.removeListener('event', handler);
// Wyczyść timery
clearInterval(intervalId);
// Nullify referencje
largeObject = null;`
},
{
title: 'Użyj WeakMap/WeakSet',
description: 'Użyj słabych referencji dla pamięci podręcznych',
example: 'const cache = new WeakMap(); // Pozwala na garbage collection'
},
{
title: 'Strumieniuj duże dane',
description: 'Przetwarzaj duże zestawy danych w kawałkach',
example: 'Użyj strumieni zamiast ładowania całych plików do pamięci'
}
]
});
}
}
calculateMemoryGrowth() {
const samples = this.data.memory.samples;
if (samples.length < 2) return { rate: 0, total: 0, leaks: [] };
const first = samples[0];
const last = samples[samples.length - 1];
const totalGrowth = last.heapUsed - first.heapUsed;
const rate = totalGrowth / first.heapUsed;
// Wykryj potencjalne źródła wycieków
const leaks = [];
if (this.data.memory.eventListeners > 100) {
leaks.push('Nadmierna liczba nasłuchiwaczy zdarzeń');
}
if (this.data.memory.timers > 50) {
leaks.push('Wiele aktywnych timerów');
}
return { rate, total: totalGrowth, leaks };
}
async analyzeNetworkLatency() {
const slowRequests = this.data.network.requests
.filter(r => r.duration > 1000) // > 1 sekunda
.sort((a, b) => b.duration - a.duration);
if (slowRequests.length > 0) {
const avgLatency = slowRequests.reduce((sum, r) => sum + r.duration, 0) / slowRequests.length;
this.bottlenecks.push({
type: 'network',
severity: avgLatency > 3000 ? 'critical' : 'high',
impact: slowRequests.length / this.data.network.requests.length * 100,
details: {
slowRequests: slowRequests.slice(0, 5),
avgLatency
},
recommendations: [
{
title: 'Zaimplementuj buforowanie',
description: 'Buforuj często dostępne zasoby',
example: 'Dodaj nagłówki Cache-Control i zaimplementuj buforowanie service worker'
},
{
title: 'Użyj CDN',
description: 'Obsługuj statyczne zasoby z CDN',
example: 'Przenieś obrazy, CSS i JS do CDN dla szybszego dostarczania'
},
{
title: 'Włącz kompresję',
description: 'Kompresuj odpowiedzi za pomocą gzip/brotli',
example: 'app.use(compression()); // Middleware kompresji Express'
},
{
title: 'Optymalizuj payloady',
description: 'Zmniejsz rozmiary odpowiedzi',
example: 'Użyj paginacji, GraphQL lub sparse fieldsets'
}
]
});
}
}
async analyzeRenderingPerformance() {
if (!this.data.frontend) return;
const slowComponents = this.data.frontend.components
.filter(c => c.avgRenderTime > 16) // Wolniejsze niż 60fps
.sort((a, b) => b.totalTime - a.totalTime);
if (slowComponents.length > 0) {
this.bottlenecks.push({
type: 'rendering',
severity: slowComponents[0].avgRenderTime > 50 ? 'critical' : 'high',
impact: slowComponents[0].totalTime / this.data.totalTime * 100,
details: {
slowComponents: slowComponents.slice(0, 5)
},
recommendations: [
{
title: 'Optymalizuj ponowne renderowanie',
description: 'Użyj React.memo i useMemo',
example: `
const MemoizedComponent = React.memo(Component, (prevProps, nextProps) => {
return prevProps.id === nextProps.id;
});`
},
{
title: 'Wirtualizuj listy',
description: 'Użyj wirtualnego przewijania dla długich list',
example: 'Zaimplementuj react-window lub react-virtualized'
},
{
title: 'Dziel kod',
description: 'Leniwie ładuj komponenty',
example: `
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));`
}
]
});
}
}
generateReport() {
return {
summary: {
totalBottlenecks: this.bottlenecks.length,
criticalCount: this.bottlenecks.filter(b => b.severity === 'critical').length,
estimatedImpact: this.bottlenecks.reduce((sum, b) => sum + b.impact, 0)
},
bottlenecks: this.bottlenecks,
actionPlan: this.generateActionPlan()
};
}
generateActionPlan() {
const plan = [];
// Grupuj według typu i ważności
const critical = this.bottlenecks.filter(b => b.severity === 'critical');
const high = this.bottlenecks.filter(b => b.severity === 'high');
if (critical.length > 0) {
plan.push({
phase: 'Natychmiastowe',
tasks: critical.map(b => ({
type: b.type,
action: b.recommendations[0].title,
impact: `Oczekiwana poprawa o ${b.impact.toFixed(1)}%`
}))
});
}
if (high.length > 0) {
plan.push({
phase: 'Krótkoterminowe',
tasks: high.map(b => ({
type: b.type,
action: b.recommendations[0].title,
impact: `Oczekiwana poprawa o ${b.impact.toFixed(1)}%`
}))
});
}
return plan;
}
}
> Optymalizuj nasze zapytania do bazy danych:
> - Przeanalizuj wolne zapytania
> - Dodaj odpowiednie indeksy
> - Przepisz nieefektywne zapytania
> - Zaimplementuj buforowanie zapytań
> Pokaż metryki wydajności przed i po
query-optimizer.js
class QueryOptimizer {
constructor(db) {
this.db = db;
}
async optimizeQueries(slowQueries) {
const optimizations = [];
for (const query of slowQueries) {
const optimization = await this.optimizeQuery(query);
if (optimization) {
optimizations.push(optimization);
}
}
return optimizations;
}
async optimizeQuery(queryInfo) {
const { query, avgTime, count } = queryInfo;
// Analizuj strukturę zapytania
const analysis = this.analyzeQuery(query);
// Generuj strategie optymalizacji
const strategies = [];
// 1. Optymalizacja indeksów
if (analysis.missingIndexes.length > 0) {
strategies.push({
type: 'index',
description: 'Dodaj brakujące indeksy',
implementation: analysis.missingIndexes.map(idx =>
`CREATE INDEX ${idx.name} ON ${idx.table}(${idx.columns.join(', ')});`
),
estimatedImprovement: 70
});
}
// 2. Przepisanie zapytania
if (analysis.inefficientPatterns.length > 0) {
strategies.push({
type: 'rewrite',
description: 'Przepisz nieefektywne wzorce zapytań',
implementation: this.rewriteQuery(query, analysis.inefficientPatterns),
estimatedImprovement: 50
});
}
// 3. Buforowanie
if (count > 10 && analysis.cacheable) {
strategies.push({
type: 'cache',
description: 'Zaimplementuj buforowanie wyników zapytań',
implementation: this.generateCachingCode(query),
estimatedImprovement: 90
});
}
// Testuj optymalizacje
const results = await this.testOptimizations(query, strategies);
return {
original: {
query,
avgTime,
count
},
optimizations: results,
bestStrategy: results.reduce((best, current) =>
current.improvement > best.improvement ? current : best
)
};
}
analyzeQuery(query) {
const analysis = {
tables: [],
joins: [],
whereConditions: [],
orderBy: [],
missingIndexes: [],
inefficientPatterns: [],
cacheable: true
};
// Wyodrębnij komponenty zapytania
const tableMatches = query.match(/FROM\s+(\w+)/gi);
if (tableMatches) {
analysis.tables = tableMatches.map(m => m.replace(/FROM\s+/i, ''));
}
// Sprawdź brakujące indeksy w warunkach WHERE
const whereMatch = query.match(/WHERE\s+(.+?)(?:ORDER|GROUP|LIMIT|$)/i);
if (whereMatch) {
const conditions = whereMatch[1].split(/\s+AND\s+/i);
for (const condition of conditions) {
const columnMatch = condition.match(/(\w+)\s*=|(\w+)\s+IN|(\w+)\s+LIKE/i);
if (columnMatch) {
const column = columnMatch[1] || columnMatch[2] || columnMatch[3];
analysis.whereConditions.push(column);
// Sprawdź czy indeks istnieje (uproszczone sprawdzenie)
const indexExists = await this.checkIndexExists(analysis.tables[0], column);
if (!indexExists) {
analysis.missingIndexes.push({
name: `idx_${analysis.tables[0]}_${column}`,
table: analysis.tables[0],
columns: [column]
});
}
}
}
}
// Sprawdź nieefektywne wzorce
if (query.includes('SELECT *')) {
analysis.inefficientPatterns.push('select_all');
}
if (query.match(/NOT\s+IN\s*\(/i)) {
analysis.inefficientPatterns.push('not_in');
}
if (query.match(/LIKE\s+'%[^']+'/i)) {
analysis.inefficientPatterns.push('leading_wildcard');
}
// Sprawdź czy może być buforowane
if (query.match(/NOW\(\)|CURRENT_|RAND\(\)/i)) {
analysis.cacheable = false;
}
return analysis;
}
async checkIndexExists(table, column) {
// Uproszczone sprawdzenie - w praktyce zapytaj katalogi systemowe
try {
const result = await this.db.query(`
SELECT 1 FROM pg_indexes
WHERE tablename = $1
AND indexdef LIKE '%${column}%'
`, [table]);
return result.rows.length > 0;
} catch {
return false;
}
}
rewriteQuery(query, patterns) {
let optimized = query;
for (const pattern of patterns) {
switch (pattern) {
case 'select_all':
// Zastąp SELECT * konkretnymi kolumnami
optimized = optimized.replace(
/SELECT\s+\*/i,
'SELECT id, name, created_at' // Przykładowe kolumny
);
break;
case 'not_in':
// Zastąp NOT IN za pomocą LEFT JOIN
optimized = optimized.replace(
/WHERE\s+(\w+)\s+NOT\s+IN\s*\(([^)]+)\)/i,
(match, column, subquery) =>
`LEFT JOIN (${subquery}) excluded ON main.${column} = excluded.${column} WHERE excluded.${column} IS NULL`
);
break;
case 'leading_wildcard':
// Zasugeruj wyszukiwanie pełnotekstowe
optimized = '-- Rozważ użycie wyszukiwania pełnotekstowego zamiast LIKE z początkowym wildcardzie\n' + optimized;
break;
}
}
return optimized;
}
generateCachingCode(query) {
const cacheKey = this.generateCacheKey(query);
return `
// Implementacja buforowania Redis
const cacheKey = '${cacheKey}';
const cacheTTL = 3600; // 1 godzina
async function getCachedQuery(params) {
// Sprawdź cache najpierw
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
// Wykonaj zapytanie
const result = await db.query(\`${query}\`, params);
// Buforuj wynik
await redis.setex(cacheKey, cacheTTL, JSON.stringify(result.rows));
return result.rows;
}
// Unieważnij cache przy zmianach danych
async function invalidateCache() {
await redis.del(cacheKey);
}`;
}
generateCacheKey(query) {
// Generuj stabilny klucz cache z zapytania
const normalized = query
.replace(/\s+/g, ' ')
.trim()
.toLowerCase();
return `query:${crypto.createHash('md5').update(normalized).digest('hex')}`;
}
async testOptimizations(originalQuery, strategies) {
const results = [];
for (const strategy of strategies) {
try {
// Testuj optymalizację
const testResult = await this.benchmarkQuery(
strategy.type === 'rewrite' ? strategy.implementation : originalQuery,
strategy
);
results.push({
...strategy,
actualImprovement: testResult.improvement,
newAvgTime: testResult.avgTime,
successful: true
});
} catch (error) {
results.push({
...strategy,
error: error.message,
successful: false
});
}
}
return results;
}
async benchmarkQuery(query, optimization) {
const iterations = 10;
const times = [];
// Uruchom zapytanie wielokrotnie
for (let i = 0; i < iterations; i++) {
const start = performance.now();
await this.db.query(query);
times.push(performance.now() - start);
}
const avgTime = times.reduce((a, b) => a + b) / times.length;
const improvement = optimization.estimatedImprovement || 0;
return { avgTime, improvement };
}
}
> Optymalizuj kod naszej aplikacji pod kątem wydajności:
> - Zidentyfikuj funkcje intensywnie wykorzystujące CPU
> - Optymalizuj algorytmy
> - Zaimplementuj buforowanie
> - Dodaj równoległość
> - Zmniejsz alokacje pamięci
code-optimizer.js
class CodeOptimizer {
analyzeAndOptimize(code, profilerData) {
const optimizations = [];
// 1. Optymalizacja algorytmów
const algorithmOptimizations = this.optimizeAlgorithms(code, profilerData);
optimizations.push(...algorithmOptimizations);
// 2. Możliwości buforowania
const cachingOptimizations = this.addCaching(code, profilerData);
optimizations.push(...cachingOptimizations);
// 3. Równoległość
const parallelOptimizations = this.addParallelization(code);
optimizations.push(...parallelOptimizations);
// 4. Optymalizacja pamięci
const memoryOptimizations = this.optimizeMemory(code);
optimizations.push(...memoryOptimizations);
return optimizations;
}
optimizeAlgorithms(code, profilerData) {
const optimizations = [];
// Przykład: Zastąp O(n²) za pomocą O(n log n)
if (code.includes('filter') && code.includes('includes')) {
optimizations.push({
type: 'algorithm',
pattern: 'nested array search',
original: `
// Złożoność O(n²)
const filtered = array1.filter(item =>
array2.includes(item.id)
);`,
optimized: `
// Złożoność O(n) używając Set
const array2Set = new Set(array2);
const filtered = array1.filter(item =>
array2Set.has(item.id)
);`,
improvement: 'Z O(n²) do O(n)'
});
}
// Przykład: Optymalizuj sortowanie
if (code.match(/sort\([^)]*\).*sort\([^)]*\)/)) {
optimizations.push({
type: 'algorithm',
pattern: 'multiple sorts',
original: `
// Wielokrotne sortowania
data.sort((a, b) => a.date - b.date)
.sort((a, b) => a.priority - b.priority);`,
optimized: `
// Pojedyncze kombinowane sortowanie
data.sort((a, b) => {
if (a.priority !== b.priority) {
return a.priority - b.priority;
}
return a.date - b.date;
});`,
improvement: 'Zmniejszone z 2 przejść do 1'
});
}
return optimizations;
}
addCaching(code, profilerData) {
const optimizations = [];
// Memoizacja dla kosztownych funkcji
const expensiveFunctions = profilerData.functions
.filter(f => f.avgTime > 10 && f.calls > 10);
for (const func of expensiveFunctions) {
optimizations.push({
type: 'caching',
pattern: 'memoization',
original: `
function ${func.name}(input) {
// Kosztowne obliczenie
return result;
}`,
optimized: `
const ${func.name} = (() => {
const cache = new Map();
return function(input) {
const key = JSON.stringify(input);
if (cache.has(key)) {
return cache.get(key);
}
// Kosztowne obliczenie
const result = computeResult(input);
cache.set(key, result);
// Wykluczanie LRU
if (cache.size > 1000) {
const firstKey = cache.keys().next().value;
cache.delete(firstKey);
}
return result;
};
})();`,
improvement: 'Unikaj powtarzających się kalkulacji'
});
}
return optimizations;
}
addParallelization(code) {
const optimizations = [];
// Szukaj sekwencyjnych operacji async
if (code.match(/await.*\n.*await/)) {
optimizations.push({
type: 'parallelization',
pattern: 'sequential awaits',
original: `
// Wykonanie sekwencyjne
const user = await fetchUser(id);
const posts = await fetchPosts(id);
const comments = await fetchComments(id);`,
optimized: `
// Wykonanie równoległe
const [user, posts, comments] = await Promise.all([
fetchUser(id),
fetchPosts(id),
fetchComments(id)
]);`,
improvement: '3x szybciej dla niezależnych operacji'
});
}
// Worker threads dla zadań intensywnie wykorzystujących CPU
if (code.includes('for') && code.includes('compute')) {
optimizations.push({
type: 'parallelization',
pattern: 'cpu-intensive loop',
original: `
// Przetwarzanie jednowątkowe
const results = [];
for (const item of largeArray) {
results.push(expensiveComputation(item));
}`,
optimized: `
// Przetwarzanie wielowątkowe
const { Worker } = require('worker_threads');
const os = require('os');
async function parallelProcess(items) {
const numWorkers = os.cpus().length;
const chunkSize = Math.ceil(items.length / numWorkers);
const chunks = [];
for (let i = 0; i < items.length; i += chunkSize) {
chunks.push(items.slice(i, i + chunkSize));
}
const workers = chunks.map(chunk =>
new Promise((resolve, reject) => {
const worker = new Worker('./computation-worker.js');
worker.postMessage(chunk);
worker.on('message', resolve);
worker.on('error', reject);
})
);
const results = await Promise.all(workers);
return results.flat();
}`,
improvement: `${os.cpus().length}x przyspiesznie dla zadań CPU-bound`
});
}
return optimizations;
}
optimizeMemory(code) {
const optimizations = [];
// Object pooling
if (code.includes('new') && code.includes('loop')) {
optimizations.push({
type: 'memory',
pattern: 'object allocation in loop',
original: `
// Tworzy śmieci
for (let i = 0; i < 1000000; i++) {
const obj = new ExpensiveObject();
process(obj);
}`,
optimized: `
// Pool obiektów
class ObjectPool {
constructor(createFn, resetFn, size = 100) {
this.createFn = createFn;
this.resetFn = resetFn;
this.pool = [];
this.available = [];
// Wstępnie wypełnij pool
for (let i = 0; i < size; i++) {
const obj = createFn();
this.pool.push(obj);
this.available.push(obj);
}
}
acquire() {
if (this.available.length > 0) {
return this.available.pop();
}
return this.createFn();
}
release(obj) {
this.resetFn(obj);
if (this.available.length < this.pool.length) {
this.available.push(obj);
}
}
}
const objectPool = new ObjectPool(
() => new ExpensiveObject(),
(obj) => obj.reset()
);
for (let i = 0; i < 1000000; i++) {
const obj = objectPool.acquire();
process(obj);
objectPool.release(obj);
}`,
improvement: 'Zmniejszona presja GC i narzut alokacji'
});
}
// Łączenie stringów
if (code.match(/\+=.*string|str\s*\+/)) {
optimizations.push({
type: 'memory',
pattern: 'string concatenation',
original: `
// Nieefektywne budowanie stringów
let result = '';
for (const item of items) {
result += item + ',';
}`,
optimized: `
// Efektywne budowanie stringów
const parts = [];
for (const item of items) {
parts.push(item);
}
const result = parts.join(',');
// Lub dla bardzo dużych stringów
const { StringDecoder } = require('string_decoder');
const decoder = new StringDecoder('utf8');
const buffers = [];
// ... dodaj buffers
const result = Buffer.concat(buffers).toString();`,
improvement: 'Unikaj pośrednich alokacji stringów'
});
}
return optimizations;
}
}
> Utwórz kompleksowy zestaw testów obciążeniowych:
> - Symuluj realistyczne zachowanie użytkowników
> - Testuj pod różnymi warunkami obciążenia
> - Zidentyfikuj punkty krytyczne
> - Generuj raporty wydajności
load-tester.js
const autocannon = require('autocannon');
const { Worker } = require('worker_threads');
class LoadTester {
constructor(config) {
this.config = {
baseUrl: config.baseUrl || 'http://localhost:3000',
duration: config.duration || 60,
scenarios: config.scenarios || []
};
this.results = [];
}
async runLoadTests() {
console.log('Rozpoczynam testy obciążeniowe...\n');
// Uruchom różne scenariusze obciążenia
for (const scenario of this.config.scenarios) {
console.log(`Uruchamiam scenariusz: ${scenario.name}`);
const result = await this.runScenario(scenario);
this.results.push(result);
// Ochłodzenie między scenariuszami
await this.coolDown(10);
}
return this.generateReport();
}
async runScenario(scenario) {
const instance = autocannon({
url: this.config.baseUrl + scenario.endpoint,
connections: scenario.connections || 10,
pipelining: scenario.pipelining || 1,
duration: scenario.duration || this.config.duration,
requests: scenario.requests,
setupClient: this.setupClient.bind(this),
// Niestandardowa logika scenariusza
requests: this.generateRequests(scenario)
});
return new Promise((resolve) => {
instance.on('done', (results) => {
resolve({
scenario: scenario.name,
results: this.processResults(results),
metrics: this.calculateMetrics(results)
});
});
});
}
setupClient(client) {
// Dodaj uwierzytelnianie jeśli potrzebne
client.on('headers', (headers) => {
headers['Authorization'] = 'Bearer ' + this.config.authToken;
});
}
generateRequests(scenario) {
const requests = [];
// Generuj realistyczne wzorce żądań
if (scenario.type === 'user-journey') {
requests.push(
// Login
{
method: 'POST',
path: '/api/auth/login',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: 'test@example.com',
password: 'password123'
})
},
// Przeglądaj produkty
{
method: 'GET',
path: '/api/products?page=1&limit=20',
setupRequest: (req, context) => {
req.headers['Authorization'] = `Bearer ${context.token}`;
}
},
// Dodaj do koszyka
{
method: 'POST',
path: '/api/cart/add',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
productId: '123',
quantity: 1
})
}
);
}
return requests;
}
processResults(results) {
return {
requests: {
total: results.requests.total,
average: results.requests.average,
mean: results.requests.mean,
stddev: results.requests.stddev,
min: results.requests.min,
max: results.requests.max,
p99: results.requests.p99,
p95: results.requests.p95,
p50: results.requests.p50
},
throughput: {
average: results.throughput.average,
mean: results.throughput.mean,
stddev: results.throughput.stddev,
min: results.throughput.min,
max: results.throughput.max,
p99: results.throughput.p99
},
latency: {
average: results.latency.average,
mean: results.latency.mean,
stddev: results.latency.stddev,
min: results.latency.min,
max: results.latency.max,
p99: results.latency.p99,
p95: results.latency.p95,
p50: results.latency.p50
},
errors: results.errors,
timeouts: results.timeouts,
duration: results.duration,
connections: results.connections,
pipelining: results.pipelining
};
}
calculateMetrics(results) {
const metrics = {
// Żądania na sekundę
rps: results.requests.average,
// Wskaźnik sukcesu
successRate: ((results.requests.total - results.errors) / results.requests.total) * 100,
// Średni czas odpowiedzi
avgResponseTime: results.latency.mean,
// Percentylowe czasy odpowiedzi
p50ResponseTime: results.latency.p50,
p95ResponseTime: results.latency.p95,
p99ResponseTime: results.latency.p99,
// Przepustowość
throughputMBps: results.throughput.mean / 1024 / 1024,
// Wskaźnik błędów
errorRate: (results.errors / results.requests.total) * 100,
// Wskaźnik timeoutów
timeoutRate: (results.timeouts / results.requests.total) * 100
};
// Oblicz zgodność z SLA
metrics.slaCompliance = this.calculateSLACompliance(metrics);
return metrics;
}
calculateSLACompliance(metrics) {
const sla = {
maxResponseTime: 500, // ms
minSuccessRate: 99.9, // %
maxErrorRate: 0.1 // %
};
const compliance = {
responseTime: metrics.p95ResponseTime <= sla.maxResponseTime,
successRate: metrics.successRate >= sla.minSuccessRate,
errorRate: metrics.errorRate <= sla.maxErrorRate,
overall: true
};
compliance.overall = compliance.responseTime &&
compliance.successRate &&
compliance.errorRate;
return compliance;
}
async coolDown(seconds) {
console.log(`Ochładzanie przez ${seconds} sekund...`);
await new Promise(resolve => setTimeout(resolve, seconds * 1000));
}
generateReport() {
const report = {
timestamp: new Date().toISOString(),
summary: this.generateSummary(),
scenarios: this.results,
recommendations: this.generateRecommendations()
};
// Generuj wizualizacje
report.charts = this.generateCharts();
return report;
}
generateSummary() {
const summary = {
totalScenarios: this.results.length,
overallSuccess: true,
failedScenarios: [],
performance: {
avgRPS: 0,
avgResponseTime: 0,
avgSuccessRate: 0
}
};
let totalRPS = 0;
let totalResponseTime = 0;
let totalSuccessRate = 0;
for (const result of this.results) {
totalRPS += result.metrics.rps;
totalResponseTime += result.metrics.avgResponseTime;
totalSuccessRate += result.metrics.successRate;
if (!result.metrics.slaCompliance.overall) {
summary.overallSuccess = false;
summary.failedScenarios.push(result.scenario);
}
}
summary.performance.avgRPS = totalRPS / this.results.length;
summary.performance.avgResponseTime = totalResponseTime / this.results.length;
summary.performance.avgSuccessRate = totalSuccessRate / this.results.length;
return summary;
}
generateRecommendations() {
const recommendations = [];
for (const result of this.results) {
const metrics = result.metrics;
// Rekomendacje dla czasu odpowiedzi
if (metrics.p95ResponseTime > 1000) {
recommendations.push({
scenario: result.scenario,
issue: 'Wysoki czas odpowiedzi',
recommendation: 'Optymalizuj wolne endpointy, dodaj buforowanie lub skaluj horyzontalnie',
severity: 'high'
});
}
// Rekomendacje dla wskaźnika błędów
if (metrics.errorRate > 1) {
recommendations.push({
scenario: result.scenario,
issue: 'Wysoki wskaźnik błędów',
recommendation: 'Zbadaj logi błędów, napraw bugi, dodaj logikę ponawiania',
severity: 'critical'
});
}
// Rekomendacje dla przepustowości
if (metrics.rps < 100) {
recommendations.push({
scenario: result.scenario,
issue: 'Niska przepustowość',
recommendation: 'Zwiększ pulę połączeń, optymalizuj zapytania do bazy danych, dodaj buforowanie',
severity: 'medium'
});
}
}
return recommendations;
}
generateCharts() {
// Generuj dane wykresów do wizualizacji
return {
responseTimeChart: {
type: 'line',
data: this.results.map(r => ({
scenario: r.scenario,
p50: r.results.latency.p50,
p95: r.results.latency.p95,
p99: r.results.latency.p99
}))
},
throughputChart: {
type: 'bar',
data: this.results.map(r => ({
scenario: r.scenario,
rps: r.metrics.rps
}))
},
errorRateChart: {
type: 'bar',
data: this.results.map(r => ({
scenario: r.scenario,
errorRate: r.metrics.errorRate
}))
}
};
}
}
// Użycie
const loadTester = new LoadTester({
baseUrl: 'http://localhost:3000',
duration: 60,
scenarios: [
{
name: 'Lekkie obciążenie',
endpoint: '/api/products',
connections: 10,
duration: 30
},
{
name: 'Normalne obciążenie',
endpoint: '/api/products',
connections: 100,
duration: 60
},
{
name: 'Ciężkie obciążenie',
endpoint: '/api/products',
connections: 500,
duration: 60
},
{
name: 'Test skoków',
endpoint: '/api/products',
connections: 1000,
duration: 30
},
{
name: 'Podróż użytkownika',
type: 'user-journey',
connections: 50,
duration: 120
}
]
});
loadTester.runLoadTests().then(report => {
console.log(JSON.stringify(report, null, 2));
});
> Skonfiguruj ciągłe monitorowanie wydajności:
> - Zbieranie metryk w czasie rzeczywistym
> - Budżety wydajności
> - Automatyczne alerty
> - Analiza trendów
> - Wykrywanie regresji
performance-dashboard.js
class PerformanceDashboard {
constructor() {
this.metrics = {
realtime: new Map(),
historical: [],
budgets: this.loadBudgets(),
alerts: []
};
this.startMonitoring();
}
loadBudgets() {
return {
responseTime: {
p50: 200,
p95: 500,
p99: 1000
},
errorRate: 0.1, // 0.1%
cpu: 70, // 70%
memory: 80, // 80%
bundleSize: {
js: 500 * 1024, // 500KB
css: 100 * 1024, // 100KB
total: 1024 * 1024 // 1MB
},
webVitals: {
LCP: 2500,
FID: 100,
CLS: 0.1
}
};
}
startMonitoring() {
// Zbieraj metryki co minutę
setInterval(() => {
this.collectMetrics();
this.checkBudgets();
this.detectRegressions();
}, 60000);
// Monitorowanie w czasie rzeczywistym
this.setupRealtimeMonitoring();
}
async collectMetrics() {
const timestamp = Date.now();
const metrics = {
timestamp,
server: await this.collectServerMetrics(),
database: await this.collectDatabaseMetrics(),
frontend: await this.collectFrontendMetrics(),
business: await this.collectBusinessMetrics()
};
this.metrics.historical.push(metrics);
// Zachowaj tylko ostatnie 24 godziny
const dayAgo = timestamp - 24 * 60 * 60 * 1000;
this.metrics.historical = this.metrics.historical
.filter(m => m.timestamp > dayAgo);
}
async collectServerMetrics() {
// Zbieraj z endpointów monitorowania
const response = await fetch('/metrics');
const data = await response.json();
return {
responseTime: {
p50: data.http_request_duration_p50,
p95: data.http_request_duration_p95,
p99: data.http_request_duration_p99
},
requestRate: data.http_requests_per_second,
errorRate: data.http_error_rate,
cpu: data.cpu_usage_percent,
memory: data.memory_usage_percent,
activeConnections: data.active_connections
};
}
async collectDatabaseMetrics() {
const response = await fetch('/metrics/database');
const data = await response.json();
return {
queryTime: {
avg: data.avg_query_time,
p95: data.p95_query_time
},
activeConnections: data.active_connections,
slowQueries: data.slow_query_count,
deadlocks: data.deadlock_count
};
}
checkBudgets() {
const latest = this.metrics.historical[this.metrics.historical.length - 1];
if (!latest) return;
const violations = [];
// Sprawdź budżety czasu odpowiedzi
if (latest.server.responseTime.p95 > this.metrics.budgets.responseTime.p95) {
violations.push({
metric: 'Response Time P95',
value: latest.server.responseTime.p95,
budget: this.metrics.budgets.responseTime.p95,
severity: 'high'
});
}
// Sprawdź wskaźnik błędów
if (latest.server.errorRate > this.metrics.budgets.errorRate) {
violations.push({
metric: 'Error Rate',
value: latest.server.errorRate,
budget: this.metrics.budgets.errorRate,
severity: 'critical'
});
}
// Sprawdź wykorzystanie zasobów
if (latest.server.cpu > this.metrics.budgets.cpu) {
violations.push({
metric: 'CPU Usage',
value: latest.server.cpu,
budget: this.metrics.budgets.cpu,
severity: 'medium'
});
}
if (violations.length > 0) {
this.handleBudgetViolations(violations);
}
}
handleBudgetViolations(violations) {
for (const violation of violations) {
// Utwórz alert
const alert = {
id: Date.now() + Math.random(),
timestamp: Date.now(),
type: 'budget_violation',
...violation
};
this.metrics.alerts.push(alert);
// Wyślij powiadomienia
if (violation.severity === 'critical') {
this.sendAlert(alert);
}
}
}
detectRegressions() {
if (this.metrics.historical.length < 10) return;
// Porównaj obecną wydajność z historyczną bazą
const recent = this.metrics.historical.slice(-5);
const baseline = this.metrics.historical.slice(-50, -10);
const recentAvg = this.calculateAverages(recent);
const baselineAvg = this.calculateAverages(baseline);
// Wykryj znaczące regresje
const regressions = [];
// Regresja czasu odpowiedzi
const responseTimeIncrease =
(recentAvg.responseTime - baselineAvg.responseTime) / baselineAvg.responseTime;
if (responseTimeIncrease > 0.2) { // 20% wzrost
regressions.push({
metric: 'Response Time',
baseline: baselineAvg.responseTime,
current: recentAvg.responseTime,
change: `+${(responseTimeIncrease * 100).toFixed(1)}%`,
severity: responseTimeIncrease > 0.5 ? 'high' : 'medium'
});
}
// Regresja wskaźnika błędów
if (recentAvg.errorRate > baselineAvg.errorRate * 2) {
regressions.push({
metric: 'Error Rate',
baseline: baselineAvg.errorRate,
current: recentAvg.errorRate,
change: `+${((recentAvg.errorRate - baselineAvg.errorRate) * 100).toFixed(2)}%`,
severity: 'high'
});
}
if (regressions.length > 0) {
this.handleRegressions(regressions);
}
}
calculateAverages(metrics) {
const avg = {
responseTime: 0,
errorRate: 0,
cpu: 0,
memory: 0
};
for (const metric of metrics) {
avg.responseTime += metric.server.responseTime.p95;
avg.errorRate += metric.server.errorRate;
avg.cpu += metric.server.cpu;
avg.memory += metric.server.memory;
}
const count = metrics.length;
avg.responseTime /= count;
avg.errorRate /= count;
avg.cpu /= count;
avg.memory /= count;
return avg;
}
setupRealtimeMonitoring() {
// Połączenie WebSocket dla metryk czasu rzeczywistego
const ws = new WebSocket('ws://localhost:3000/metrics/stream');
ws.on('message', (data) => {
const metric = JSON.parse(data);
// Aktualizuj metryki czasu rzeczywistego
this.metrics.realtime.set(metric.type, metric);
// Sprawdź natychmiastowe problemy
if (metric.type === 'error_spike') {
this.handleErrorSpike(metric);
}
});
}
generateDashboard() {
const latest = this.metrics.historical[this.metrics.historical.length - 1];
const realtime = Object.fromEntries(this.metrics.realtime);
return {
timestamp: Date.now(),
current: {
server: latest?.server || {},
database: latest?.database || {},
realtime
},
trends: this.calculateTrends(),
alerts: this.metrics.alerts.slice(-10),
recommendations: this.generateRecommendations()
};
}
calculateTrends() {
if (this.metrics.historical.length < 2) return {};
const current = this.metrics.historical[this.metrics.historical.length - 1];
const hourAgo = this.metrics.historical[this.metrics.historical.length - 60] ||
this.metrics.historical[0];
return {
responseTime: {
current: current.server.responseTime.p95,
hourAgo: hourAgo.server.responseTime.p95,
trend: current.server.responseTime.p95 > hourAgo.server.responseTime.p95 ? 'up' : 'down'
},
errorRate: {
current: current.server.errorRate,
hourAgo: hourAgo.server.errorRate,
trend: current.server.errorRate > hourAgo.server.errorRate ? 'up' : 'down'
},
traffic: {
current: current.server.requestRate,
hourAgo: hourAgo.server.requestRate,
trend: current.server.requestRate > hourAgo.server.requestRate ? 'up' : 'down'
}
};
}
generateRecommendations() {
const recommendations = [];
const latest = this.metrics.historical[this.metrics.historical.length - 1];
if (!latest) return recommendations;
// Wysokie wykorzystanie CPU
if (latest.server.cpu > 80) {
recommendations.push({
type: 'scaling',
priority: 'high',
message: 'Wykorzystanie CPU jest wysokie. Rozważ skalowanie horyzontalne lub optymalizację kodu.',
action: 'Skaluj instancje w górę lub optymalizuj operacje intensywnie wykorzystujące CPU'
});
}
// Wolne zapytania do bazy danych
if (latest.database.slowQueries > 10) {
recommendations.push({
type: 'database',
priority: 'medium',
message: `Wykryto ${latest.database.slowQueries} wolnych zapytań.`,
action: 'Przejrzyj i optymalizuj wolne zapytania do bazy danych'
});
}
// Presja pamięci
if (latest.server.memory > 85) {
recommendations.push({
type: 'memory',
priority: 'high',
message: 'Wykorzystanie pamięci zbliża się do limitów.',
action: 'Zbadaj wycieki pamięci lub zwiększ alokację pamięci'
});
}
return recommendations;
}
}

Nauczyłeś się, jak wykorzystać Claude Code do kompleksowej analizy i optymalizacji wydajności. Kluczem jest traktowanie wydajności jako ciągłego procesu, a nie jednorazowej optymalizacji. Buduj świadomość wydajności w każdej linii kodu, każdej decyzji architektonicznej, każdym wdrożeniu.

Pamiętaj: Wydajność to funkcja. Użytkownicy nie obchodzi, jak sprytny jest Twój kod, jeśli jest wolny. Użyj Claude Code do systematycznego znajdowania i naprawiania wąskich gardeł, zapewniając, że Twoja aplikacja zachwyca użytkowników swoją szybkością i responsywnością.