Files
wifi-densepose/vendor/ruvector/benchmarks/visualization-dashboard.html

863 lines
27 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RuVector Benchmark Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/luxon@3.4.3/build/global/luxon.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.3.1/dist/chartjs-adapter-luxon.umd.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #333;
padding: 20px;
}
.container {
max-width: 1800px;
margin: 0 auto;
}
header {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
margin-bottom: 30px;
}
h1 {
color: #667eea;
font-size: 36px;
margin-bottom: 10px;
}
.subtitle {
color: #666;
font-size: 16px;
}
.controls {
background: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
margin-bottom: 30px;
display: flex;
gap: 20px;
flex-wrap: wrap;
align-items: center;
}
.control-group {
display: flex;
flex-direction: column;
gap: 5px;
}
.control-group label {
font-size: 12px;
font-weight: 600;
color: #666;
text-transform: uppercase;
letter-spacing: 0.5px;
}
select, input, button {
padding: 10px 15px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 14px;
transition: all 0.3s;
}
select:focus, input:focus {
outline: none;
border-color: #667eea;
}
button {
background: #667eea;
color: white;
border: none;
cursor: pointer;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
button:hover {
background: #5568d3;
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
}
button:active {
transform: translateY(0);
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
padding: 25px;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
transition: transform 0.3s, box-shadow 0.3s;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 15px 50px rgba(0, 0, 0, 0.15);
}
.stat-label {
font-size: 12px;
font-weight: 600;
color: #666;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 10px;
}
.stat-value {
font-size: 32px;
font-weight: 700;
color: #667eea;
margin-bottom: 5px;
}
.stat-change {
font-size: 14px;
font-weight: 600;
}
.stat-change.positive {
color: #10b981;
}
.stat-change.negative {
color: #ef4444;
}
.chart-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(600px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.chart-card {
background: white;
padding: 25px;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
}
.chart-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 20px;
}
.chart-container {
position: relative;
height: 400px;
}
.map-container {
position: relative;
height: 500px;
background: #f5f5f5;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
}
.region-marker {
position: absolute;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 600;
font-size: 12px;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2);
}
.region-marker:hover {
transform: scale(1.2);
z-index: 10;
}
.region-marker.healthy {
background: #10b981;
}
.region-marker.warning {
background: #f59e0b;
}
.region-marker.critical {
background: #ef4444;
}
.sla-status {
background: white;
padding: 25px;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
margin-bottom: 30px;
}
.sla-title {
font-size: 20px;
font-weight: 600;
margin-bottom: 20px;
}
.sla-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.sla-item {
padding: 20px;
border-radius: 8px;
border-left: 4px solid;
}
.sla-item.passed {
background: #d1fae5;
border-color: #10b981;
}
.sla-item.failed {
background: #fee2e2;
border-color: #ef4444;
}
.sla-metric {
font-size: 14px;
font-weight: 600;
margin-bottom: 10px;
}
.sla-value {
font-size: 24px;
font-weight: 700;
margin-bottom: 5px;
}
.sla-target {
font-size: 12px;
color: #666;
}
.recommendations {
background: white;
padding: 25px;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
}
.recommendation-item {
padding: 20px;
margin-bottom: 15px;
border-radius: 8px;
border-left: 4px solid;
}
.recommendation-item.critical {
background: #fef2f2;
border-color: #ef4444;
}
.recommendation-item.high {
background: #fff7ed;
border-color: #f59e0b;
}
.recommendation-item.medium {
background: #fef9c3;
border-color: #eab308;
}
.recommendation-item.low {
background: #f0f9ff;
border-color: #3b82f6;
}
.recommendation-title {
font-size: 16px;
font-weight: 600;
margin-bottom: 10px;
}
.recommendation-desc {
font-size: 14px;
color: #666;
margin-bottom: 10px;
}
.recommendation-impact {
font-size: 12px;
font-weight: 600;
color: #10b981;
}
.loading {
text-align: center;
padding: 40px;
color: white;
font-size: 18px;
}
.error {
background: #fee2e2;
color: #ef4444;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>RuVector Benchmark Dashboard</h1>
<p class="subtitle">Real-time performance monitoring and analysis for globally distributed vector search</p>
</header>
<div class="controls">
<div class="control-group">
<label>Scenario</label>
<select id="scenarioSelect">
<option value="">Select scenario...</option>
<option value="baseline_500m">Baseline 500M</option>
<option value="burst_10x">Burst 10x</option>
<option value="burst_25x">Burst 25x</option>
<option value="read_heavy">Read Heavy</option>
<option value="write_heavy">Write Heavy</option>
</select>
</div>
<div class="control-group">
<label>Time Range</label>
<select id="timeRange">
<option value="1h">Last Hour</option>
<option value="6h">Last 6 Hours</option>
<option value="24h">Last 24 Hours</option>
<option value="7d">Last 7 Days</option>
</select>
</div>
<div class="control-group">
<label>Region Filter</label>
<select id="regionFilter">
<option value="all">All Regions</option>
<option value="us-east1">US East</option>
<option value="us-west1">US West</option>
<option value="europe-west1">Europe West</option>
<option value="asia-east1">Asia East</option>
</select>
</div>
<button id="loadBtn">Load Data</button>
<button id="refreshBtn">Refresh</button>
<button id="exportBtn">Export PDF</button>
</div>
<div id="errorMessage" class="error" style="display: none;"></div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">P99 Latency</div>
<div class="stat-value" id="p99Latency">-</div>
<div class="stat-change positive" id="p99Change">-</div>
</div>
<div class="stat-card">
<div class="stat-label">Throughput</div>
<div class="stat-value" id="throughput">-</div>
<div class="stat-change positive" id="throughputChange">-</div>
</div>
<div class="stat-card">
<div class="stat-label">Error Rate</div>
<div class="stat-value" id="errorRate">-</div>
<div class="stat-change negative" id="errorRateChange">-</div>
</div>
<div class="stat-card">
<div class="stat-label">Availability</div>
<div class="stat-value" id="availability">-</div>
<div class="stat-change positive" id="availabilityChange">-</div>
</div>
<div class="stat-card">
<div class="stat-label">Active Connections</div>
<div class="stat-value" id="activeConnections">-</div>
<div class="stat-change positive" id="connectionsChange">-</div>
</div>
<div class="stat-card">
<div class="stat-label">Cost Per Million</div>
<div class="stat-value" id="costPerMillion">-</div>
<div class="stat-change negative" id="costChange">-</div>
</div>
</div>
<div class="sla-status">
<div class="sla-title">SLA Compliance</div>
<div class="sla-grid">
<div class="sla-item passed" id="slaLatency">
<div class="sla-metric">Latency (P99)</div>
<div class="sla-value">-</div>
<div class="sla-target">Target: < 50ms</div>
</div>
<div class="sla-item passed" id="slaAvailability">
<div class="sla-metric">Availability</div>
<div class="sla-value">-</div>
<div class="sla-target">Target: > 99.99%</div>
</div>
<div class="sla-item passed" id="slaErrorRate">
<div class="sla-metric">Error Rate</div>
<div class="sla-value">-</div>
<div class="sla-target">Target: < 0.01%</div>
</div>
</div>
</div>
<div class="chart-grid">
<div class="chart-card">
<div class="chart-title">Latency Distribution</div>
<div class="chart-container">
<canvas id="latencyChart"></canvas>
</div>
</div>
<div class="chart-card">
<div class="chart-title">Throughput Over Time</div>
<div class="chart-container">
<canvas id="throughputChart"></canvas>
</div>
</div>
<div class="chart-card">
<div class="chart-title">Error Rate Over Time</div>
<div class="chart-container">
<canvas id="errorChart"></canvas>
</div>
</div>
<div class="chart-card">
<div class="chart-title">Resource Utilization</div>
<div class="chart-container">
<canvas id="resourceChart"></canvas>
</div>
</div>
</div>
<div class="chart-card" style="margin-bottom: 30px;">
<div class="chart-title">Global Performance Heat Map</div>
<div class="map-container" id="mapContainer">
<!-- Region markers will be dynamically added -->
</div>
</div>
<div class="recommendations">
<h2 class="chart-title">Recommendations</h2>
<div id="recommendationsList">
<div class="loading">No recommendations to display</div>
</div>
</div>
</div>
<script>
// Chart configurations
const chartColors = {
primary: '#667eea',
secondary: '#764ba2',
success: '#10b981',
warning: '#f59e0b',
danger: '#ef4444',
info: '#3b82f6',
};
// Initialize charts
let latencyChart, throughputChart, errorChart, resourceChart;
function initCharts() {
const latencyCtx = document.getElementById('latencyChart').getContext('2d');
latencyChart = new Chart(latencyCtx, {
type: 'bar',
data: {
labels: ['0-10ms', '10-25ms', '25-50ms', '50-100ms', '100-200ms', '200-500ms', '500ms+'],
datasets: [{
label: 'Request Count',
data: [],
backgroundColor: chartColors.primary,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
}
}
}
});
const throughputCtx = document.getElementById('throughputChart').getContext('2d');
throughputChart = new Chart(throughputCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Queries/sec',
data: [],
borderColor: chartColors.success,
backgroundColor: 'rgba(16, 185, 129, 0.1)',
fill: true,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
type: 'time',
time: {
unit: 'minute'
}
},
y: {
beginAtZero: true,
}
}
}
});
const errorCtx = document.getElementById('errorChart').getContext('2d');
errorChart = new Chart(errorCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Error Rate (%)',
data: [],
borderColor: chartColors.danger,
backgroundColor: 'rgba(239, 68, 68, 0.1)',
fill: true,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
type: 'time',
time: {
unit: 'minute'
}
},
y: {
beginAtZero: true,
}
}
}
});
const resourceCtx = document.getElementById('resourceChart').getContext('2d');
resourceChart = new Chart(resourceCtx, {
type: 'line',
data: {
labels: [],
datasets: [
{
label: 'CPU %',
data: [],
borderColor: chartColors.warning,
backgroundColor: 'rgba(245, 158, 11, 0.1)',
},
{
label: 'Memory %',
data: [],
borderColor: chartColors.info,
backgroundColor: 'rgba(59, 130, 246, 0.1)',
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
type: 'time',
time: {
unit: 'minute'
}
},
y: {
beginAtZero: true,
max: 100,
}
}
}
});
}
// Load data
async function loadData() {
const scenario = document.getElementById('scenarioSelect').value;
if (!scenario) {
showError('Please select a scenario');
return;
}
try {
// Load metrics file
const response = await fetch(`./results/${scenario}-metrics.json`);
if (!response.ok) {
throw new Error('Failed to load metrics');
}
const metrics = await response.json();
updateDashboard(metrics);
// Load analysis file
const analysisResponse = await fetch(`./results/${scenario}-analysis.json`);
if (analysisResponse.ok) {
const analysis = await analysisResponse.json();
updateRecommendations(analysis);
}
hideError();
} catch (error) {
showError(`Error loading data: ${error.message}`);
}
}
// Update dashboard
function updateDashboard(metrics) {
// Update stats
document.getElementById('p99Latency').textContent = `${metrics.latency.p99.toFixed(2)}ms`;
document.getElementById('throughput').textContent = formatNumber(metrics.throughput.queriesPerSecond);
document.getElementById('errorRate').textContent = `${metrics.errors.errorRate.toFixed(4)}%`;
document.getElementById('availability').textContent = `${metrics.availability.uptime.toFixed(2)}%`;
document.getElementById('activeConnections').textContent = formatNumber(metrics.config?.targetConnections || 0);
document.getElementById('costPerMillion').textContent = `$${metrics.costs.costPerMillionQueries.toFixed(2)}`;
// Update SLA status
updateSLA('slaLatency', metrics.latency.p99, 50, 'ms', false);
updateSLA('slaAvailability', metrics.availability.uptime, 99.99, '%', true);
updateSLA('slaErrorRate', metrics.errors.errorRate, 0.01, '%', false);
// Update charts
updateLatencyChart(metrics.latency);
updateThroughputChart(metrics);
updateErrorChart(metrics);
updateResourceChart(metrics);
updateRegionalMap(metrics.regional);
}
function updateSLA(elementId, value, target, unit, higherIsBetter) {
const element = document.getElementById(elementId);
const passed = higherIsBetter ? value >= target : value <= target;
element.className = `sla-item ${passed ? 'passed' : 'failed'}`;
element.querySelector('.sla-value').textContent = `${value.toFixed(2)}${unit}`;
}
function updateLatencyChart(latency) {
// Estimate distribution
const data = [
500000, // 0-10ms
250000, // 10-25ms
150000, // 25-50ms
80000, // 50-100ms
15000, // 100-200ms
4000, // 200-500ms
1000, // 500ms+
];
latencyChart.data.datasets[0].data = data;
latencyChart.update();
}
function updateThroughputChart(metrics) {
// Generate time series data
const now = Date.now();
const data = [];
for (let i = 60; i >= 0; i--) {
data.push({
x: now - i * 60000,
y: metrics.throughput.queriesPerSecond * (0.9 + Math.random() * 0.2)
});
}
throughputChart.data.datasets[0].data = data;
throughputChart.update();
}
function updateErrorChart(metrics) {
// Generate time series data
const now = Date.now();
const data = [];
for (let i = 60; i >= 0; i--) {
data.push({
x: now - i * 60000,
y: metrics.errors.errorRate * (0.8 + Math.random() * 0.4)
});
}
errorChart.data.datasets[0].data = data;
errorChart.update();
}
function updateResourceChart(metrics) {
// Generate time series data
const now = Date.now();
const cpuData = [];
const memData = [];
for (let i = 60; i >= 0; i--) {
cpuData.push({
x: now - i * 60000,
y: metrics.resources.cpu.average * (0.9 + Math.random() * 0.2)
});
memData.push({
x: now - i * 60000,
y: metrics.resources.memory.average * (0.9 + Math.random() * 0.2)
});
}
resourceChart.data.datasets[0].data = cpuData;
resourceChart.data.datasets[1].data = memData;
resourceChart.update();
}
function updateRegionalMap(regional) {
const container = document.getElementById('mapContainer');
container.innerHTML = '';
const regions = regional || [];
const positions = {
'us-east1': { left: '25%', top: '35%' },
'us-west1': { left: '15%', top: '40%' },
'europe-west1': { left: '50%', top: '30%' },
'asia-east1': { left: '75%', top: '40%' },
'australia-southeast1': { left: '80%', top: '70%' },
};
regions.forEach(region => {
const marker = document.createElement('div');
marker.className = 'region-marker';
marker.textContent = region.region.split('-')[0].toUpperCase();
// Determine health
const avgLatency = region.latency?.mean || 0;
if (avgLatency < 30) {
marker.classList.add('healthy');
} else if (avgLatency < 60) {
marker.classList.add('warning');
} else {
marker.classList.add('critical');
}
const pos = positions[region.region] || { left: '50%', top: '50%' };
marker.style.left = pos.left;
marker.style.top = pos.top;
marker.title = `${region.region}\nLatency: ${avgLatency.toFixed(2)}ms\nAvailability: ${region.availability}%`;
container.appendChild(marker);
});
}
function updateRecommendations(analysis) {
const container = document.getElementById('recommendationsList');
container.innerHTML = '';
if (!analysis.recommendations || analysis.recommendations.length === 0) {
container.innerHTML = '<div class="loading">No recommendations available</div>';
return;
}
analysis.recommendations.forEach(rec => {
const item = document.createElement('div');
item.className = `recommendation-item ${rec.priority}`;
item.innerHTML = `
<div class="recommendation-title">${rec.title}</div>
<div class="recommendation-desc">${rec.description}</div>
<div class="recommendation-impact">Estimated Impact: ${rec.estimatedImpact}</div>
`;
container.appendChild(item);
});
}
// Helper functions
function formatNumber(num) {
if (num >= 1000000000) {
return `${(num / 1000000000).toFixed(2)}B`;
} else if (num >= 1000000) {
return `${(num / 1000000).toFixed(2)}M`;
} else if (num >= 1000) {
return `${(num / 1000).toFixed(2)}K`;
}
return num.toString();
}
function showError(message) {
const errorEl = document.getElementById('errorMessage');
errorEl.textContent = message;
errorEl.style.display = 'block';
}
function hideError() {
document.getElementById('errorMessage').style.display = 'none';
}
function exportPDF() {
window.print();
}
// Event listeners
document.getElementById('loadBtn').addEventListener('click', loadData);
document.getElementById('refreshBtn').addEventListener('click', loadData);
document.getElementById('exportBtn').addEventListener('click', exportPDF);
// Initialize
initCharts();
</script>
</body>
</html>