diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index 207191cbd..bd31ab0c3 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -77,7 +77,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue public function __construct(public ScheduledDatabaseBackup $backup) { - $this->onQueue('high'); + $this->onQueue(crons_queue()); $this->timeout = $backup->timeout ?? 3600; } diff --git a/app/Jobs/ScheduledJobManager.php b/app/Jobs/ScheduledJobManager.php index a601186bf..bd8ee2819 100644 --- a/app/Jobs/ScheduledJobManager.php +++ b/app/Jobs/ScheduledJobManager.php @@ -37,18 +37,7 @@ class ScheduledJobManager implements ShouldQueue */ public function __construct() { - $this->onQueue($this->determineQueue()); - } - - /** - * On cloud this job runs on a dedicated `crons` queue so it can be drained by an isolated - * Horizon worker pool; self-hosted keeps it on the shared `high` queue. Routing is decided - * by `isCloud()` (config-based), so the dispatching process needs no special env — only - * the worker must be configured to drain `crons` via `HORIZON_QUEUES`. - */ - private function determineQueue(): string - { - return isCloud() ? 'crons' : 'high'; + $this->onQueue(crons_queue()); } /** diff --git a/app/Jobs/ScheduledTaskJob.php b/app/Jobs/ScheduledTaskJob.php index 49b9b9702..67ef1ebe3 100644 --- a/app/Jobs/ScheduledTaskJob.php +++ b/app/Jobs/ScheduledTaskJob.php @@ -65,7 +65,7 @@ class ScheduledTaskJob implements ShouldBeEncrypted, ShouldQueue public function __construct($task) { - $this->onQueue('high'); + $this->onQueue(crons_queue()); $this->task = $task; if ($service = $task->service()->first()) { diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 582e6750d..4bb989de4 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -608,6 +608,23 @@ function deployment_queue(): string return isCloud() ? 'deployments' : 'high'; } +/** + * Resolve the queue used for scheduled jobs — the scheduler dispatcher, scheduled tasks and + * scheduled database backups, whether triggered automatically or manually. + * + * On cloud these jobs run on a dedicated `crons` queue so they can be drained by an isolated + * Horizon worker pool; self-hosted keeps them on the shared `high` queue. Routing is decided + * by `isCloud()` (config-based), so the dispatching process needs no special env — only the + * worker must be configured to drain `crons`. + * + * IMPORTANT: on cloud a worker MUST include `crons` in its `HORIZON_QUEUES`, otherwise these + * jobs are never processed. + */ +function crons_queue(): string +{ + return isCloud() ? 'crons' : 'high'; +} + function translate_cron_expression($expression_to_validate): string { if (isset(VALID_CRON_STRINGS[$expression_to_validate])) { diff --git a/tests/Feature/QueueRoutingTest.php b/tests/Feature/QueueRoutingTest.php index 1552c6bb3..1bb837be8 100644 --- a/tests/Feature/QueueRoutingTest.php +++ b/tests/Feature/QueueRoutingTest.php @@ -3,7 +3,9 @@ use App\Actions\Database\StartDatabase; use App\Actions\Database\StartDatabaseProxy; use App\Actions\Service\StartService; +use App\Jobs\DatabaseBackupJob; use App\Jobs\ScheduledJobManager; +use App\Models\ScheduledDatabaseBackup; describe('deployment_queue helper', function () { test('uses the high queue on self-hosted', function () { @@ -19,6 +21,20 @@ describe('deployment_queue helper', function () { }); }); +describe('crons_queue helper', function () { + test('uses the high queue on self-hosted', function () { + config(['constants.coolify.self_hosted' => true]); + + expect(crons_queue())->toBe('high'); + }); + + test('uses the crons queue on cloud', function () { + config(['constants.coolify.self_hosted' => false]); + + expect(crons_queue())->toBe('crons'); + }); +}); + describe('start action job routing', function () { test('routes to the deployments queue on cloud', function (string $actionClass) { config(['constants.coolify.self_hosted' => false]); @@ -41,16 +57,18 @@ describe('start action job routing', function () { ]); }); -describe('scheduled job manager queue routing', function () { - test('uses the crons queue on cloud', function () { +describe('scheduled job routing', function () { + test('scheduled jobs use the crons queue on cloud', function () { config(['constants.coolify.self_hosted' => false]); expect((new ScheduledJobManager)->queue)->toBe('crons'); + expect((new DatabaseBackupJob(new ScheduledDatabaseBackup))->queue)->toBe('crons'); }); - test('uses the high queue on self-hosted', function () { + test('scheduled jobs use the high queue on self-hosted', function () { config(['constants.coolify.self_hosted' => true]); expect((new ScheduledJobManager)->queue)->toBe('high'); + expect((new DatabaseBackupJob(new ScheduledDatabaseBackup))->queue)->toBe('high'); }); });