From f1f7272494303533cf6e6b7d7a121fd58f0e29f6 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Sat, 3 Oct 2026 20:50:49 +0200 Subject: [PATCH] fix(queue): run service and database start commands on the deployment queue On cloud, StartService and DeployServiceApplication dispatched their CoolifyTask on the high queue, so service starts competed with all other high jobs. remote_process() and CoolifyTask now accept a queue (default high); start paths pass deployment_queue(). The queued RestartDatabase action also runs on deployment_queue() instead of the default queue. --- app/Actions/Database/RestartDatabase.php | 6 ++++++ .../Service/DeployServiceApplication.php | 2 +- app/Actions/Service/StartService.php | 2 +- app/Jobs/CoolifyTask.php | 4 +++- app/Traits/ExecutesDatabaseStartCommands.php | 2 +- bootstrap/helpers/remoteProcess.php | 2 ++ .../ContentFilesBeforeComposeUpTest.php | 20 +++++++++++++++++++ .../Jobs/RemoteProcessCommandStorageTest.php | 16 +++++++++++++++ tests/Feature/QueueRoutingTest.php | 3 +++ 9 files changed, 53 insertions(+), 4 deletions(-) diff --git a/app/Actions/Database/RestartDatabase.php b/app/Actions/Database/RestartDatabase.php index 66c7c8c961..19e78587b1 100644 --- a/app/Actions/Database/RestartDatabase.php +++ b/app/Actions/Database/RestartDatabase.php @@ -15,11 +15,17 @@ use App\Models\StandaloneSqlite; use App\Support\DatabaseOperationReservation; use App\Support\ResourceStartActivity; use Lorisleiva\Actions\Concerns\AsAction; +use Lorisleiva\Actions\Decorators\JobDecorator; class RestartDatabase { use AsAction; + public function configureJob(JobDecorator $job): void + { + $job->onQueue(deployment_queue()); + } + /** * @param string|null $reservation The token from StartDatabase::reserveOperation(). The request * that queued this action holds the reservation; this action diff --git a/app/Actions/Service/DeployServiceApplication.php b/app/Actions/Service/DeployServiceApplication.php index 78039a8252..c89c8e1e85 100644 --- a/app/Actions/Service/DeployServiceApplication.php +++ b/app/Actions/Service/DeployServiceApplication.php @@ -64,6 +64,6 @@ class DeployServiceApplication $commands->push("docker network connect --alias {$networkAlias} {$network} {$containerName} >/dev/null 2>&1 || true"); } - return remote_process($commands->toArray(), $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged'); + return remote_process($commands->toArray(), $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged', queue: deployment_queue()); } } diff --git a/app/Actions/Service/StartService.php b/app/Actions/Service/StartService.php index d928866f1d..1580045d09 100644 --- a/app/Actions/Service/StartService.php +++ b/app/Actions/Service/StartService.php @@ -64,7 +64,7 @@ class StartService } $commands = array_merge($commands, $this->logDrainNetworkConnectCommands($service)); - return remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged'); + return remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged', queue: deployment_queue()); } /** diff --git a/app/Jobs/CoolifyTask.php b/app/Jobs/CoolifyTask.php index 44739cd3f1..cac687fc85 100755 --- a/app/Jobs/CoolifyTask.php +++ b/app/Jobs/CoolifyTask.php @@ -38,6 +38,7 @@ class CoolifyTask implements ShouldBeEncrypted, ShouldQueue * Create a new job instance. * * @param int|null $timeout Job timeout in seconds; null keeps the default above. + * @param string $queue Queue to run on; start actions pass deployment_queue(). */ public function __construct( public Activity $activity, @@ -45,12 +46,13 @@ class CoolifyTask implements ShouldBeEncrypted, ShouldQueue public $call_event_on_finish, public $call_event_data, ?int $timeout = null, + string $queue = 'high', ) { if ($timeout !== null) { $this->timeout = $timeout; } - $this->onQueue('high'); + $this->onQueue($queue); } /** diff --git a/app/Traits/ExecutesDatabaseStartCommands.php b/app/Traits/ExecutesDatabaseStartCommands.php index d267a8b1b2..fca00cafa0 100644 --- a/app/Traits/ExecutesDatabaseStartCommands.php +++ b/app/Traits/ExecutesDatabaseStartCommands.php @@ -14,6 +14,6 @@ trait ExecutesDatabaseStartCommands return app(DatabaseStartCommandExecutor::class)->execute($commands, $database, $activity); } - return remote_process($commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged'); + return remote_process($commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged', queue: deployment_queue()); } } diff --git a/bootstrap/helpers/remoteProcess.php b/bootstrap/helpers/remoteProcess.php index b583521221..74f5215f4a 100644 --- a/bootstrap/helpers/remoteProcess.php +++ b/bootstrap/helpers/remoteProcess.php @@ -29,6 +29,7 @@ function remote_process( $callEventData = null, array $properties = [], ?int $timeout = null, + string $queue = 'high', ): Activity { $type = $type ?? ActivityTypes::INLINE->value; $command = $command instanceof Collection ? $command->toArray() : $command; @@ -74,6 +75,7 @@ function remote_process( call_event_on_finish: $callEventOnFinish, call_event_data: $callEventData, timeout: $timeout, + queue: $queue, )); $activity->refresh(); diff --git a/tests/Feature/ContentFilesBeforeComposeUpTest.php b/tests/Feature/ContentFilesBeforeComposeUpTest.php index 2086f9ce88..0838b84ca8 100644 --- a/tests/Feature/ContentFilesBeforeComposeUpTest.php +++ b/tests/Feature/ContentFilesBeforeComposeUpTest.php @@ -4,6 +4,7 @@ use App\Actions\Service\DeployServiceApplication; use App\Actions\Service\StartService; use App\Actions\Shared\EnsureContentFilesOnServer; use App\Jobs\ApplicationDeploymentJob; +use App\Jobs\CoolifyTask; use App\Jobs\ServerStorageSaveJob; use App\Models\Application; use App\Models\ApplicationDeploymentQueue; @@ -579,3 +580,22 @@ test('the server file sync still marks a storage without content as a directory' expect($volume->fresh()->is_directory)->toBeTruthy(); }); + +test('starting a service runs its commands on the deployment queue', function (bool $selfHosted, string $queue) { + config(['constants.coolify.self_hosted' => $selfHosted]); + $service = Service::factory()->create([ + 'environment_id' => $this->environment->id, + 'server_id' => $this->server->id, + 'destination_id' => $this->destination->id, + 'destination_type' => $this->destination->getMorphClass(), + 'docker_compose_raw' => "services:\n app:\n image: nginx:alpine\n", + ]); + fakeContentFilesServer([]); + + StartService::run($service); + + Bus::assertDispatched(CoolifyTask::class, fn (CoolifyTask $job) => $job->queue === $queue); +})->with([ + 'cloud' => [false, 'deployments'], + 'self-hosted' => [true, 'high'], +]); diff --git a/tests/Feature/Jobs/RemoteProcessCommandStorageTest.php b/tests/Feature/Jobs/RemoteProcessCommandStorageTest.php index a9fde57d5e..664b5fbe8b 100644 --- a/tests/Feature/Jobs/RemoteProcessCommandStorageTest.php +++ b/tests/Feature/Jobs/RemoteProcessCommandStorageTest.php @@ -85,3 +85,19 @@ it('removes the command when the task fails permanently', function () { expect($stored->getExtraProperty('command'))->toBeNull() ->and($stored->getExtraProperty('status'))->toBe(ProcessStatus::ERROR->value); }); + +it('queues a remote process on the high queue by default', function () { + Queue::fake(); + + remote_process(['echo ok'], $this->server); + + Queue::assertPushedOn('high', CoolifyTask::class); +}); + +it('queues a remote process on the requested queue', function () { + Queue::fake(); + + remote_process(['echo ok'], $this->server, queue: 'deployments'); + + Queue::assertPushedOn('deployments', CoolifyTask::class); +}); diff --git a/tests/Feature/QueueRoutingTest.php b/tests/Feature/QueueRoutingTest.php index 1bb837be89..717b4a5b41 100644 --- a/tests/Feature/QueueRoutingTest.php +++ b/tests/Feature/QueueRoutingTest.php @@ -1,5 +1,6 @@ queue)->toBe('deployments'); })->with([ StartDatabase::class, + RestartDatabase::class, StartDatabaseProxy::class, StartService::class, ]); @@ -52,6 +54,7 @@ describe('start action job routing', function () { expect($actionClass::makeJob()->queue)->toBe('high'); })->with([ StartDatabase::class, + RestartDatabase::class, StartDatabaseProxy::class, StartService::class, ]);