mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
perf(metrics): ask Sentinel to bucket metric history rows
Send bucket_ms on the Sentinel metric history reads so Sentinel averages the rows and sends far fewer of them over SSH. Older Sentinels ignore the param and return raw rows, which Coolify still buckets and downsamples. Also quote the history URL in the single-fetch curl command, because the URL now has an & in it.
This commit is contained in:
committed by
Andras Bacsai
parent
1d286427ad
commit
5312ea4dbc
@@ -35,6 +35,17 @@ class SentinelMetricsClient
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sentinel averages rows into `bucket_ms` windows. Older Sentinels ignore the param
|
||||
* and return raw rows, which seriesRows() buckets the same way.
|
||||
*
|
||||
* @return array{from: string, bucket_ms: int}
|
||||
*/
|
||||
private static function historyQuery(string $range): array
|
||||
{
|
||||
return ['from' => self::rangeFrom($range), 'bucket_ms' => self::bucketMs($range)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
@@ -112,7 +123,7 @@ class SentinelMetricsClient
|
||||
default => ['/cpu/history', 'percent'],
|
||||
};
|
||||
|
||||
return $this->seriesRows($this->url($path, ['from' => self::rangeFrom($range)]), $field, $range, $metric === 'disk');
|
||||
return $this->seriesRows($this->url($path, self::historyQuery($range)), $field, $range, $metric === 'disk');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,7 +131,7 @@ class SentinelMetricsClient
|
||||
*/
|
||||
public function networkHistory(string $range): array
|
||||
{
|
||||
$url = $this->url('/network/history', ['from' => self::rangeFrom($range)]);
|
||||
$url = $this->url('/network/history', self::historyQuery($range));
|
||||
|
||||
return [
|
||||
'rx' => $this->seriesRows($url, 'rxBytesPerSec', $range),
|
||||
@@ -149,8 +160,9 @@ class SentinelMetricsClient
|
||||
$rows = array_filter($rows, fn ($row) => ($row['mount'] ?? null) === $mount);
|
||||
}
|
||||
|
||||
// Average raw samples into epoch-aligned buckets so every server shares the same
|
||||
// timestamps and the fleet aggregator can sum them point by point.
|
||||
// Average samples into epoch-aligned buckets so every server shares the same
|
||||
// timestamps and the fleet aggregator can sum them point by point. A no-op for
|
||||
// rows Sentinel already bucketed.
|
||||
$bucketMs = self::bucketMs($range);
|
||||
$sums = [];
|
||||
$counts = [];
|
||||
|
||||
@@ -35,7 +35,7 @@ trait HasMetrics
|
||||
// only has letters, digits and `._-+/=` (ServerSetting::isValidSentinelToken), so it
|
||||
// cannot end the heredoc. Responses are separated by a 0x1E record separator.
|
||||
$script = implode("\nprintf '\\036'\n", array_map(
|
||||
fn (string $type) => 'curl -s -H @- "'.$this->getMetricsEndpoint($type, $from)."\" <<'COOLIFY_SENTINEL_AUTH'\nAuthorization: Bearer {$token}\nCOOLIFY_SENTINEL_AUTH",
|
||||
fn (string $type) => 'curl -s -H @- "'.$this->getMetricsEndpoint($type, $from, $mins)."\" <<'COOLIFY_SENTINEL_AUTH'\nAuthorization: Bearer {$token}\nCOOLIFY_SENTINEL_AUTH",
|
||||
$types
|
||||
));
|
||||
|
||||
@@ -147,11 +147,11 @@ trait HasMetrics
|
||||
unset($this->prefetchedMetricResponses[$key]);
|
||||
} else {
|
||||
$from = now()->subMinutes($mins)->toIso8601ZuluString();
|
||||
$endpoint = $this->getMetricsEndpoint($type, $from);
|
||||
$endpoint = $this->getMetricsEndpoint($type, $from, $mins);
|
||||
$token = $this->metricsToken($server);
|
||||
|
||||
$response = instant_remote_process(
|
||||
["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$token}\" {$endpoint}'"],
|
||||
["docker exec coolify-sentinel sh -c 'curl -H \"Authorization: Bearer {$token}\" \"{$endpoint}\"'"],
|
||||
$server,
|
||||
false
|
||||
);
|
||||
@@ -248,13 +248,23 @@ trait HasMetrics
|
||||
return $this->isServerMetrics() ? $this : $this->destination->server;
|
||||
}
|
||||
|
||||
private function getMetricsEndpoint(string $type, string $from): string
|
||||
/**
|
||||
* Ranges over an hour ask Sentinel to average rows into whole-minute windows, at most
|
||||
* 1000 per range, so it sends far fewer rows. Older Sentinels ignore `bucket_ms`;
|
||||
* mapRows() still downsamples their raw rows.
|
||||
*/
|
||||
private function getMetricsEndpoint(string $type, string $from, int $mins): string
|
||||
{
|
||||
$base = 'http://localhost:8888/api';
|
||||
if ($this->isServerMetrics()) {
|
||||
return "{$base}/{$type}/history?from={$from}";
|
||||
$query = "from={$from}";
|
||||
if ($mins > 60) {
|
||||
$query .= '&bucket_ms='.((int) ceil($mins / 1000) * 60_000);
|
||||
}
|
||||
|
||||
return "{$base}/container/{$this->uuid}/{$type}/history?from={$from}";
|
||||
if ($this->isServerMetrics()) {
|
||||
return "{$base}/{$type}/history?{$query}";
|
||||
}
|
||||
|
||||
return "{$base}/container/{$this->uuid}/{$type}/history?{$query}";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user