← back to Handbag Auth Nextjs
auction-viewer/benchmark-api.js
289 lines
#!/usr/bin/env node
/**
* API Performance Benchmark Tool
* Tests API response times and throughput
*/
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const BASE_URL = process.env.BASE_URL || 'http://45.61.58.125:7500';
const CONCURRENT_REQUESTS = 10;
const WARMUP_REQUESTS = 5;
class BenchmarkResults {
constructor() {
this.results = [];
}
add(endpoint, duration, statusCode, cached = false) {
this.results.push({
endpoint,
duration,
statusCode,
cached,
timestamp: Date.now()
});
}
report() {
const grouped = {};
this.results.forEach(r => {
if (!grouped[r.endpoint]) {
grouped[r.endpoint] = [];
}
grouped[r.endpoint].push(r.duration);
});
const report = {
summary: {},
details: []
};
for (const [endpoint, durations] of Object.entries(grouped)) {
const sorted = durations.sort((a, b) => a - b);
const avg = sorted.reduce((a, b) => a + b, 0) / sorted.length;
const min = sorted[0];
const max = sorted[sorted.length - 1];
const p50 = sorted[Math.floor(sorted.length * 0.5)];
const p95 = sorted[Math.floor(sorted.length * 0.95)];
const p99 = sorted[Math.floor(sorted.length * 0.99)];
report.details.push({
endpoint,
requests: sorted.length,
avg: Math.round(avg),
min: Math.round(min),
max: Math.round(max),
p50: Math.round(p50),
p95: Math.round(p95),
p99: Math.round(p99)
});
}
// Overall summary
const allDurations = this.results.map(r => r.duration);
const totalRequests = allDurations.length;
const avgDuration = allDurations.reduce((a, b) => a + b, 0) / totalRequests;
const successCount = this.results.filter(r => r.statusCode === 200).length;
report.summary = {
totalRequests,
successRate: ((successCount / totalRequests) * 100).toFixed(2) + '%',
avgResponseTime: Math.round(avgDuration) + 'ms',
throughput: (totalRequests / (this.getTotalDuration() / 1000)).toFixed(2) + ' req/s'
};
return report;
}
getTotalDuration() {
if (this.results.length === 0) return 0;
const first = Math.min(...this.results.map(r => r.timestamp));
const last = Math.max(...this.results.map(r => r.timestamp));
return last - first;
}
}
const benchmarks = new BenchmarkResults();
async function benchmark(name, url, options = {}) {
console.log(`\n🔄 Benchmarking: ${name}`);
console.log(` URL: ${url}`);
// Warmup
console.log(` Warming up (${WARMUP_REQUESTS} requests)...`);
for (let i = 0; i < WARMUP_REQUESTS; i++) {
try {
await axios.get(url, { timeout: 30000 });
} catch (error) {
console.log(` ⚠️ Warmup request failed: ${error.message}`);
}
}
// Actual benchmark
const requests = options.requests || CONCURRENT_REQUESTS;
console.log(` Running ${requests} concurrent requests...`);
const startTime = Date.now();
const promises = [];
for (let i = 0; i < requests; i++) {
promises.push(
(async () => {
const reqStart = Date.now();
try {
const response = await axios.get(url, { timeout: 30000 });
const duration = Date.now() - reqStart;
const cached = response.headers['x-cache'] === 'HIT' || duration < 10;
benchmarks.add(name, duration, response.status, cached);
return {
success: true,
duration,
status: response.status,
cached
};
} catch (error) {
const duration = Date.now() - reqStart;
benchmarks.add(name, duration, error.response?.status || 0);
return {
success: false,
duration,
error: error.message
};
}
})()
);
}
const results = await Promise.all(promises);
const endTime = Date.now();
const totalDuration = endTime - startTime;
// Calculate stats
const successful = results.filter(r => r.success).length;
const durations = results.filter(r => r.success).map(r => r.duration);
const avgDuration = durations.reduce((a, b) => a + b, 0) / durations.length;
const minDuration = Math.min(...durations);
const maxDuration = Math.max(...durations);
const throughput = (requests / (totalDuration / 1000)).toFixed(2);
console.log(` ✅ Success: ${successful}/${requests}`);
console.log(` ⏱️ Avg: ${Math.round(avgDuration)}ms | Min: ${minDuration}ms | Max: ${maxDuration}ms`);
console.log(` 🚀 Throughput: ${throughput} req/s`);
return results;
}
async function runBenchmarks() {
console.log('═══════════════════════════════════════════════════════');
console.log(' LUXVAULT API PERFORMANCE BENCHMARK');
console.log('═══════════════════════════════════════════════════════');
console.log(`Base URL: ${BASE_URL}`);
console.log(`Concurrent Requests: ${CONCURRENT_REQUESTS}`);
console.log(`Warmup Requests: ${WARMUP_REQUESTS}`);
console.log('═══════════════════════════════════════════════════════');
try {
// Health check
console.log('\n📊 Testing health endpoint...');
await benchmark('Health Check', `${BASE_URL}/health`, { requests: 20 });
// Basic endpoints
await benchmark('Get All Auctions', `${BASE_URL}/api/v1/auctions?limit=50`);
await benchmark('Get Auctions (Paginated)', `${BASE_URL}/api/v1/auctions?limit=20&page=2`);
// Filtering
await benchmark('Filter by Brand', `${BASE_URL}/api/v1/auctions?brand=Hermes%20Birkin&limit=50`);
await benchmark('Filter by Price Range', `${BASE_URL}/api/v1/auctions?min_price=5000&max_price=20000&limit=50`);
// Sorting
await benchmark('Sort by Price', `${BASE_URL}/api/v1/auctions?sort=price&order=asc&limit=50`);
await benchmark('Sort by Savings', `${BASE_URL}/api/v1/auctions?sort=savings&order=desc&limit=50`);
// Search
await benchmark('Full-Text Search', `${BASE_URL}/api/v1/search?q=birkin&limit=50`);
// Analytics
await benchmark('Statistics', `${BASE_URL}/api/stats`);
await benchmark('Best Values', `${BASE_URL}/api/best-values?limit=50`);
await benchmark('Brand Analytics', `${BASE_URL}/api/insights/brand/Hermes%20Birkin`);
// Complex queries
await benchmark('Combined Filters', `${BASE_URL}/api/v1/auctions?brand=Chanel&min_price=1000&max_price=10000&sort=savings&limit=50`);
// Cache test (same endpoint twice)
console.log('\n🔄 Testing cache performance...');
await benchmark('Stats (First Hit)', `${BASE_URL}/api/stats`, { requests: 5 });
await benchmark('Stats (Cached)', `${BASE_URL}/api/stats`, { requests: 15 });
// Generate report
console.log('\n═══════════════════════════════════════════════════════');
console.log(' BENCHMARK RESULTS');
console.log('═══════════════════════════════════════════════════════');
const report = benchmarks.report();
console.log('\n📊 Summary:');
console.log(` Total Requests: ${report.summary.totalRequests}`);
console.log(` Success Rate: ${report.summary.successRate}`);
console.log(` Avg Response Time: ${report.summary.avgResponseTime}`);
console.log(` Throughput: ${report.summary.throughput}`);
console.log('\n📈 Endpoint Performance:');
console.log('┌─────────────────────────────────────────┬──────────┬─────┬─────┬─────┬─────┬─────┬─────┐');
console.log('│ Endpoint │ Requests │ Avg │ Min │ Max │ P50 │ P95 │ P99 │');
console.log('├─────────────────────────────────────────┼──────────┼─────┼─────┼─────┼─────┼─────┼─────┤');
report.details.forEach(detail => {
const endpoint = detail.endpoint.padEnd(39);
const requests = detail.requests.toString().padStart(8);
const avg = (detail.avg + 'ms').padStart(5);
const min = (detail.min + 'ms').padStart(5);
const max = (detail.max + 'ms').padStart(5);
const p50 = (detail.p50 + 'ms').padStart(5);
const p95 = (detail.p95 + 'ms').padStart(5);
const p99 = (detail.p99 + 'ms').padStart(5);
console.log(`│ ${endpoint} │ ${requests} │ ${avg} │ ${min} │ ${max} │ ${p50} │ ${p95} │ ${p99} │`);
});
console.log('└─────────────────────────────────────────┴──────────┴─────┴─────┴─────┴─────┴─────┴─────┘');
// Performance grades
console.log('\n🎯 Performance Grades:');
report.details.forEach(detail => {
let grade = 'A';
let emoji = '🟢';
if (detail.avg > 500) {
grade = 'C';
emoji = '🟡';
} else if (detail.avg > 200) {
grade = 'B';
emoji = '🟢';
}
if (detail.avg > 1000) {
grade = 'D';
emoji = '🔴';
}
console.log(` ${emoji} ${detail.endpoint.padEnd(40)} Grade: ${grade} (${detail.avg}ms avg)`);
});
// Save report to file
const reportPath = path.join(__dirname, 'benchmark-results.json');
fs.writeFileSync(reportPath, JSON.stringify({
timestamp: new Date().toISOString(),
baseUrl: BASE_URL,
...report
}, null, 2));
console.log(`\n📄 Full report saved to: ${reportPath}`);
console.log('\n═══════════════════════════════════════════════════════');
console.log('✅ Benchmark completed successfully!');
console.log('═══════════════════════════════════════════════════════\n');
} catch (error) {
console.error('\n❌ Benchmark failed:', error.message);
process.exit(1);
}
}
// Run benchmarks
if (require.main === module) {
runBenchmarks().catch(error => {
console.error('Fatal error:', error);
process.exit(1);
});
}
module.exports = { benchmark, runBenchmarks };