mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 21:18:11 -04:00
fix(metrics): align fleet series by time bucket, harden fleet page inputs
Address review findings on the fleet metrics page. Data correctness: - bucket raw Sentinel samples into epoch-aligned windows (5m/30m/2h per range) and average them, replacing per-server LTTB. Every server now reports the same bucket timestamps, so the fleet sums add up point by point instead of zigzagging between servers (and RX+TX really combine) - keep only the root mount in disk history, matching HasMetrics::getDiskMetrics - leave sparkline gaps as null instead of 0, so absent samples read as gaps Robustness: - normalize containerMetric in mount()/updatedContainerMetric(), so /metrics?cmetric=foo no longer throws an undefined array key - include the server uuid in the container row wire:key; system containers share one Sentinel id across servers - round the range start to the minute so the 60s history cache can hit - cap the Sentinel curl with --max-time 10, so one hung request cannot stall the serial per-server loop UI: - pass the spark time axis on first paint, so tooltips no longer show 1970 - show the active series name (RX/TX) in the server network chart tooltip
This commit is contained in:
@@ -25,12 +25,12 @@ class ChartStubClient extends SentinelMetricsClient
|
||||
return [];
|
||||
}
|
||||
|
||||
public function history(string $metric, string $from): array
|
||||
public function history(string $metric, string $range): array
|
||||
{
|
||||
return [[1000, 10.0], [2000, 20.0]];
|
||||
}
|
||||
|
||||
public function networkHistory(string $from): array
|
||||
public function networkHistory(string $range): array
|
||||
{
|
||||
return ['rx' => [[1000, 5.0]], 'tx' => [[1000, 1.0]]];
|
||||
}
|
||||
@@ -46,7 +46,7 @@ class ChartTestableMetrics extends Metrics
|
||||
|
||||
class DenseChartStubClient extends ChartStubClient
|
||||
{
|
||||
public function history(string $metric, string $from): array
|
||||
public function history(string $metric, string $range): array
|
||||
{
|
||||
// 300 points, like a downsampled 24h host series.
|
||||
return array_map(fn ($i) => [$i * 1000, (float) $i], range(1, 300));
|
||||
@@ -99,12 +99,12 @@ it('includes KPI-tile spark series aligned to one shared time axis', function ()
|
||||
$payload = $params[0];
|
||||
|
||||
// Union of every series' buckets, sorted; each spark projects onto it,
|
||||
// filling gaps with 0 (network only has the 1000 bucket → tail is 0).
|
||||
// leaving gaps as null (network only has the 1000 bucket → tail is null).
|
||||
return $payload['sparkCategories'] == [1000, 2000]
|
||||
&& $payload['cpuSpark'] == [10, 20]
|
||||
&& $payload['memSpark'] == [10, 20]
|
||||
&& $payload['diskSpark'] == [10, 20]
|
||||
&& $payload['netSpark'] == [6, 0];
|
||||
&& $payload['netSpark'] === [6, null];
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -30,12 +30,12 @@ class StubMetricsClient extends SentinelMetricsClient
|
||||
return self::$containers[$this->server->uuid] ?? [];
|
||||
}
|
||||
|
||||
public function history(string $metric, string $from): array
|
||||
public function history(string $metric, string $range): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function networkHistory(string $from): array
|
||||
public function networkHistory(string $range): array
|
||||
{
|
||||
return ['rx' => [], 'tx' => []];
|
||||
}
|
||||
@@ -189,3 +189,41 @@ it('renders container metric values without leaking a blade directive', function
|
||||
->assertDontSee('@break')
|
||||
->assertSee('/s');
|
||||
});
|
||||
|
||||
it('falls back to cpu for an unknown container metric', function () {
|
||||
$s = metricsServer($this->team, $this->privateKey);
|
||||
StubMetricsClient::$summaries = [$s->uuid => onlineSummary()];
|
||||
StubMetricsClient::$containers = [$s->uuid => [
|
||||
['id' => 'a', 'cpu' => 5.0, 'memUsed' => 900, 'memPercent' => 9.0, 'diskBytes' => 1, 'net' => 0.0],
|
||||
]];
|
||||
|
||||
Livewire::withQueryParams(['cmetric' => 'foo'])
|
||||
->test(TestableMetrics::class)
|
||||
->assertOk()
|
||||
->assertSet('containerMetric', 'cpu')
|
||||
->set('containerMetric', 'bar')
|
||||
->assertSet('containerMetric', 'cpu');
|
||||
});
|
||||
|
||||
it('keys container rows by server so shared container ids do not collide', function () {
|
||||
$one = metricsServer($this->team, $this->privateKey);
|
||||
$two = metricsServer($this->team, $this->privateKey);
|
||||
$proxy = ['id' => 'coolify-proxy', 'cpu' => 1.0, 'memUsed' => 10, 'memPercent' => 1.0, 'diskBytes' => 1, 'net' => 0.0];
|
||||
|
||||
StubMetricsClient::$summaries = [$one->uuid => onlineSummary(), $two->uuid => onlineSummary()];
|
||||
StubMetricsClient::$containers = [$one->uuid => [$proxy], $two->uuid => [$proxy]];
|
||||
|
||||
Livewire::test(TestableMetrics::class)
|
||||
->assertSeeHtml('wire:key="fleet-container-'.$one->uuid.'-coolify-proxy"')
|
||||
->assertSeeHtml('wire:key="fleet-container-'.$two->uuid.'-coolify-proxy"');
|
||||
});
|
||||
|
||||
it('passes the spark time axis to the KPI sparklines on first paint', function () {
|
||||
$s = metricsServer($this->team, $this->privateKey);
|
||||
StubMetricsClient::$summaries = [$s->uuid => onlineSummary()];
|
||||
|
||||
$component = Livewire::test(TestableMetrics::class);
|
||||
$component->set('chartData', ['sparkCategories' => [1700000100000], 'cpuSpark' => [12.5]]);
|
||||
|
||||
$component->assertSeeHtml('1700000100000');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user