From 5a2006e99c77b0316465f50196c2da6787193c63 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Wed, 30 Sep 2026 19:23:35 +0200 Subject: [PATCH] perf(docker): look up foreign preview owners once per container status check GetContainersStatus now resolves all unknown preview owners with one query, like the Sentinel push. The lookup helper also no longer fails on an application without a uuid. --- app/Actions/Docker/GetContainersStatus.php | 10 +++- bootstrap/helpers/docker.php | 6 ++- .../Feature/ContainerOwnerUuidLabelsTest.php | 51 +++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/app/Actions/Docker/GetContainersStatus.php b/app/Actions/Docker/GetContainersStatus.php index 794c581011..fbe26dd586 100644 --- a/app/Actions/Docker/GetContainersStatus.php +++ b/app/Actions/Docker/GetContainersStatus.php @@ -106,6 +106,14 @@ class GetContainersStatus $foundDatabases = []; $foundServices = []; + // Owners of preview containers outside this server's applications, in one query. + $foreignApplicationIdsByUuid = containerApplicationIdsByUuid( + $this->applications, + $this->containers + ->map(fn ($container) => format_docker_labels_to_json(data_get($container, $this->server->isSwarm() ? 'Spec.Labels' : 'Config.Labels') ?? [])) + ->filter(fn (Collection $labels) => (bool) $labels->get('coolify.pullRequestId') && isContainerOfType($labels, 'application')) + ); + foreach ($this->containers as $container) { if ($this->server->isSwarm()) { $labels = data_get($container, 'Spec.Labels'); @@ -136,7 +144,7 @@ class GetContainersStatus if ($isApplicationContainer) { $pullRequestId = data_get($labels, 'coolify.pullRequestId'); if ($pullRequestId) { - $applicationId = resolveContainerApplicationId($this->applications, $flatLabels); + $applicationId = resolveContainerApplicationId($this->applications, $flatLabels, $foreignApplicationIdsByUuid); $preview = $applicationId ? ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first() : null; diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index 8883cd0cab..b44714a8b0 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -154,14 +154,16 @@ function resolveContainerApplicationId(Collection $applications, Collection|arra */ function containerApplicationIdsByUuid(Collection $applications, iterable $containerLabels): array { - $knownUuids = $applications->pluck('uuid')->flip(); $uuids = []; foreach ($containerLabels as $labels) { $uuid = containerOwnerUuid($labels, 'application'); - if ($uuid && ! $knownUuids->has($uuid)) { + if ($uuid) { $uuids[$uuid] = true; } } + foreach ($applications->pluck('uuid')->filter() as $knownUuid) { + unset($uuids[$knownUuid]); + } if ($uuids === []) { return []; } diff --git a/tests/Feature/ContainerOwnerUuidLabelsTest.php b/tests/Feature/ContainerOwnerUuidLabelsTest.php index 67957a181d..5e46f7bab2 100644 --- a/tests/Feature/ContainerOwnerUuidLabelsTest.php +++ b/tests/Feature/ContainerOwnerUuidLabelsTest.php @@ -311,6 +311,57 @@ describe('status updates after a server transfer', function () { expect($this->application->fresh()->status)->toStartWith('running'); }); + + test('container status check looks up foreign preview owners with one query for all containers', function () { + $deleted = Application::factory()->create([ + 'environment_id' => $this->environment->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + ]); + $preview = ApplicationPreview::forceCreate([ + 'application_id' => $deleted->id, + 'pull_request_id' => 3, + 'pull_request_html_url' => 'https://example.com/pr/3', + 'status' => 'exited', + ]); + $deleted->delete(); + + $containers = collect(range(1, 5))->map(fn (int $i) => [ + 'Name' => "/foreign-app-{$i}-pr-{$i}", + 'State' => ['Status' => 'running', 'Health' => ['Status' => 'healthy']], + 'RestartCount' => 0, + 'Config' => ['Labels' => [ + 'coolify.managed' => 'true', + 'coolify.applicationUuid' => "foreign-app-{$i}", + 'coolify.pullRequestId' => (string) $i, + 'com.docker.compose.service' => "foreign-app-{$i}-pr-{$i}", + ]], + ])->push([ + 'Name' => '/'.$deleted->uuid.'-pr-3', + 'State' => ['Status' => 'running', 'Health' => ['Status' => 'healthy']], + 'RestartCount' => 0, + 'Config' => ['Labels' => [ + 'coolify.managed' => 'true', + 'coolify.applicationUuid' => $deleted->uuid, + 'coolify.pullRequestId' => '3', + 'com.docker.compose.service' => $deleted->uuid.'-pr-3', + ]], + ]); + + $this->server->settings()->update(['is_reachable' => true, 'is_usable' => true]); + $server = $this->server->fresh(); + + DB::enableQueryLog(); + GetContainersStatus::run($server, $containers, collect()); + $ownerLookups = collect(DB::getQueryLog()) + ->filter(fn (array $query) => str_contains($query['query'], 'from "applications"') && preg_match('/"uuid" (=|in) /', $query['query']) === 1) + ->count(); + DB::disableQueryLog(); + + expect($ownerLookups)->toBe(1) + // The preview of a trashed owner still resolves. + ->and($preview->fresh()->status)->toBe('running:healthy'); + }); }); describe('orphaned preview cleanup', function () {