feat: Унифицировать количество точек на графиках до ~360 для любого масштаба
- 1h: без агрегации (~360 точек) - 6h: агрегация 1 минута (~360 точек) - 24h: агрегация 4 минуты (~360 точек) - 7d: агрегация 28 минут (~360 точек) - 30d: агрегация 2 часа (~360 точек) - Formula: aggregate_minutes = period_minutes / 360 - slice ограничен до 400 точек - format времени берётся из конфига агрегации
This commit is contained in:
parent
4613a14f5a
commit
6ca570ec1d
|
|
@ -48,13 +48,8 @@ class ServerDetailController extends Model
|
|||
|
||||
// Запрос с агрегацией если нужно
|
||||
if ($groupBy) {
|
||||
// Используем агрегацию на основе aggregate_minutes
|
||||
$bucketFormat = match(true) {
|
||||
// Экранируем % для DATE_FORMAT
|
||||
$aggConfig['aggregate_minutes'] >= 60 => '%Y-%m-%d %H:00', // 1 hour+
|
||||
$aggConfig['aggregate_minutes'] >= 15 => '%Y-%m-%d %H:%i', // 15-59 min
|
||||
default => '%Y-%m-%d %H:%i:00' // 1-14 min
|
||||
};
|
||||
// Используем format из конфига
|
||||
$bucketFormat = $aggConfig['format'] ?? '%Y-%m-%d %H:%i';
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
|
|
@ -152,27 +147,34 @@ class ServerDetailController extends Model
|
|||
|
||||
private function getAggregationConfig(string $period, ?string $zoom): array
|
||||
{
|
||||
// Target: ~200-400 points on chart regardless of period
|
||||
// Target: ~360 points on chart for any period/zoom
|
||||
// Formula: aggregate_minutes = period_minutes / 360
|
||||
|
||||
if ($zoom) {
|
||||
return match($zoom) {
|
||||
// Short periods - show all points
|
||||
'1h' => ['interval' => 'INTERVAL 1 HOUR', 'groupBy' => null, 'aggregate_minutes' => 0],
|
||||
'6h' => ['interval' => 'INTERVAL 6 HOUR', 'groupBy' => null, 'aggregate_minutes' => 0],
|
||||
// Medium period - light aggregation
|
||||
'24h' => ['interval' => 'INTERVAL 24 HOUR', 'groupBy' => "GROUP BY mn.id, DATE_FORMAT(sm.created_at, '%Y-%m-%d %H:%i')", 'aggregate_minutes' => 1],
|
||||
// Long periods - strong aggregation
|
||||
'7d' => ['interval' => 'INTERVAL 7 DAY', 'groupBy' => "GROUP BY mn.id, DATE_FORMAT(sm.created_at, '%Y-%m-%d %H:%i')", 'aggregate_minutes' => 15],
|
||||
'30d' => ['interval' => 'INTERVAL 30 DAY', 'groupBy' => "GROUP BY mn.id, DATE_FORMAT(sm.created_at, '%Y-%m-%d %H:00')", 'aggregate_minutes' => 60],
|
||||
default => ['interval' => 'INTERVAL 24 HOUR', 'groupBy' => null, 'aggregate_minutes' => 0]
|
||||
// 1h = 60 min / 360 = 0.17 → no aggregation (~360 points)
|
||||
'1h' => ['interval' => 'INTERVAL 1 HOUR', 'groupBy' => null, 'aggregate_minutes' => 0, 'format' => '%Y-%m-%d %H:%i:%s'],
|
||||
// 6h = 360 min / 360 = 1 min aggregation (~360 points)
|
||||
'6h' => ['interval' => 'INTERVAL 6 HOUR', 'groupBy' => "GROUP BY mn.id, DATE_FORMAT(sm.created_at, '%Y-%m-%d %H:%i')", 'aggregate_minutes' => 1, 'format' => '%Y-%m-%d %H:%i'],
|
||||
// 24h = 1440 min / 360 = 4 min aggregation (~360 points)
|
||||
'24h' => ['interval' => 'INTERVAL 24 HOUR', 'groupBy' => "GROUP BY mn.id, DATE_FORMAT(sm.created_at, '%Y-%m-%d %H:%i')", 'aggregate_minutes' => 4, 'format' => '%Y-%m-%d %H:%i'],
|
||||
// 7d = 10080 min / 360 = 28 min aggregation (~360 points)
|
||||
'7d' => ['interval' => 'INTERVAL 7 DAY', 'groupBy' => "GROUP BY mn.id, DATE_FORMAT(sm.created_at, '%Y-%m-%d %H:%i')", 'aggregate_minutes' => 28, 'format' => '%Y-%m-%d %H:%i'],
|
||||
// 30d = 43200 min / 360 = 120 min (2 hours) aggregation (~360 points)
|
||||
'30d' => ['interval' => 'INTERVAL 30 DAY', 'groupBy' => "GROUP BY mn.id, DATE_FORMAT(sm.created_at, '%Y-%m-%d %H:00')", 'aggregate_minutes' => 120, 'format' => '%Y-%m-%d %H:00'],
|
||||
default => ['interval' => 'INTERVAL 24 HOUR', 'groupBy' => null, 'aggregate_minutes' => 0, 'format' => '%Y-%m-%d %H:%i:%s']
|
||||
};
|
||||
}
|
||||
|
||||
// Default: base period aggregation
|
||||
// Default: base period aggregation (~360 points)
|
||||
return match($period) {
|
||||
'7d' => ['interval' => 'INTERVAL 7 DAY', 'groupBy' => "GROUP BY mn.id, DATE_FORMAT(sm.created_at, '%Y-%m-%d %H:%i')", 'aggregate_minutes' => 15],
|
||||
'30d' => ['interval' => 'INTERVAL 30 DAY', 'groupBy' => "GROUP BY mn.id, DATE_FORMAT(sm.created_at, '%Y-%m-%d %H:00')", 'aggregate_minutes' => 60],
|
||||
default => ['interval' => 'INTERVAL 24 HOUR', 'groupBy' => null, 'aggregate_minutes' => 0]
|
||||
// 24h = 1440 min / 360 = 4 min
|
||||
'24h' => ['interval' => 'INTERVAL 24 HOUR', 'groupBy' => "GROUP BY mn.id, DATE_FORMAT(sm.created_at, '%Y-%m-%d %H:%i')", 'aggregate_minutes' => 4, 'format' => '%Y-%m-%d %H:%i'],
|
||||
// 7d = 10080 min / 360 = 28 min
|
||||
'7d' => ['interval' => 'INTERVAL 7 DAY', 'groupBy' => "GROUP BY mn.id, DATE_FORMAT(sm.created_at, '%Y-%m-%d %H:%i')", 'aggregate_minutes' => 28, 'format' => '%Y-%m-%d %H:%i'],
|
||||
// 30d = 43200 min / 360 = 120 min
|
||||
'30d' => ['interval' => 'INTERVAL 30 DAY', 'groupBy' => "GROUP BY mn.id, DATE_FORMAT(sm.created_at, '%Y-%m-%d %H:00')", 'aggregate_minutes' => 120, 'format' => '%Y-%m-%d %H:00'],
|
||||
default => ['interval' => 'INTERVAL 24 HOUR', 'groupBy' => null, 'aggregate_minutes' => 0, 'format' => '%Y-%m-%d %H:%i:%s']
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -498,9 +498,9 @@ const ctx{{ metricName|replace({'-': '_', '.': '_'}) }} = document.getElementByI
|
|||
var labels{{ metricName }} = [];
|
||||
var data{{ metricName }} = [];
|
||||
|
||||
{% for metric in metricData|slice(0, 50000)|reverse %}
|
||||
{% for metric in metricData|slice(0, 400)|reverse %}
|
||||
{% set time_val = metric.time_bucket|default(metric.created_at) %}
|
||||
{% set time_format = metric.time_bucket ? 'd.m H:i' : 'H:i' %}
|
||||
{% set time_format = metric.time_bucket and aggregation.aggregate_minutes >= 60 ? 'd.m H:i' : 'H:i' %}
|
||||
labels{{ metricName }}.push('{{ time_val|date(time_format) }}');
|
||||
data{{ metricName }}.push({{ metric.value|raw }});
|
||||
{% endfor %}
|
||||
|
|
|
|||
Loading…
Reference in New Issue