diff --git a/app/Console/Commands/HorizonManage.php b/app/Console/Commands/HorizonManage.php index 611b516b0a..a03c41a7ba 100644 --- a/app/Console/Commands/HorizonManage.php +++ b/app/Console/Commands/HorizonManage.php @@ -7,6 +7,7 @@ use Illuminate\Support\Facades\Artisan; use Laravel\Horizon\Contracts\JobRepository; use Laravel\Horizon\Contracts\MetricsRepository; +use Laravel\Horizon\Repositories\RedisJobRepository; use function Laravel\Prompts\multiselect; use function Laravel\Prompts\select; @@ -14,7 +15,7 @@ class HorizonManage extends Command { - protected $signature = 'horizon:manage '; + protected $signature = 'horizon:manage'; protected $description = 'Manage horizon'; @@ -28,6 +29,7 @@ public function handle() 'workers' => 'Workers', 'failed' => 'Failed Jobs', 'failed-delete' => 'Failed Jobs - Delete', + 'purge-queues' => 'Purge Queues', ] ); @@ -102,11 +104,11 @@ public function handle() return; } - dump($runningJobs); foreach ($runningJobs as $runningJob) { $runningJobsTable[] = [ 'id' => $runningJob->id, 'name' => $runningJob->name, + 'reserved_at' => $runningJob->reserved_at ? now()->parse($runningJob->reserved_at)->format('Y-m-d H:i:s') : null, ]; } table($runningJobsTable); @@ -123,5 +125,15 @@ public function handle() } table($workersTable); } + + if ($action === 'purge-queues') { + $getQueues = app(CustomJobRepository::class)->getQueues(); + $queueName = select( + label: 'Which queue to purge?', + options: $getQueues, + ); + $redisJobRepository = app(RedisJobRepository::class); + $redisJobRepository->purge($queueName); + } } } diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index bd9149040c..1b92f817b1 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -237,6 +237,7 @@ public function handle(): void { $this->application_deployment_queue->update([ 'status' => ApplicationDeploymentStatus::IN_PROGRESS->value, + 'horizon_job_worker' => gethostname(), ]); if ($this->server->isFunctional() === false) { $this->application_deployment_queue->addLogEntry('Server is not functional.'); @@ -2389,10 +2390,12 @@ private function next(string $status) queue_next_deployment($this->application); // If the deployment is cancelled by the user, don't update the status if ( - $this->application_deployment_queue->status !== ApplicationDeploymentStatus::CANCELLED_BY_USER->value && $this->application_deployment_queue->status !== ApplicationDeploymentStatus::FAILED->value + $this->application_deployment_queue->status !== ApplicationDeploymentStatus::CANCELLED_BY_USER->value && + $this->application_deployment_queue->status !== ApplicationDeploymentStatus::FAILED->value ) { $this->application_deployment_queue->update([ 'status' => $status, + 'horizon_job_status' => $status, ]); } if ($this->application_deployment_queue->status === ApplicationDeploymentStatus::FAILED->value) { diff --git a/app/Livewire/Project/Application/DeploymentNavbar.php b/app/Livewire/Project/Application/DeploymentNavbar.php index 6a6fa24823..8210a12a19 100644 --- a/app/Livewire/Project/Application/DeploymentNavbar.php +++ b/app/Livewire/Project/Application/DeploymentNavbar.php @@ -84,6 +84,7 @@ public function cancel() $this->application_deployment_queue->update([ 'current_process_id' => null, 'status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value, + 'horizon_job_status' => ApplicationDeploymentStatus::CANCELLED_BY_USER->value, ]); next_after_cancel($server); } diff --git a/app/Providers/HorizonServiceProvider.php b/app/Providers/HorizonServiceProvider.php index a3ca3fc988..e89528d4ce 100644 --- a/app/Providers/HorizonServiceProvider.php +++ b/app/Providers/HorizonServiceProvider.php @@ -3,9 +3,12 @@ namespace App\Providers; use App\Contracts\CustomJobRepositoryInterface; +use App\Models\ApplicationDeploymentQueue; use App\Repositories\CustomJobRepository; +use Illuminate\Support\Facades\Event; use Illuminate\Support\ServiceProvider; use Laravel\Horizon\Contracts\JobRepository; +use Laravel\Horizon\Events\JobReserved; class HorizonServiceProvider extends ServiceProvider { @@ -23,6 +26,22 @@ public function register(): void */ public function boot(): void { - // + // Event::listen(function (JobReserved $event) { + // $payload = $event->payload->decoded; + // $jobName = $payload['displayName']; + // if ($jobName === 'App\Jobs\ApplicationDeploymentJob') { + // $tags = $payload['tags']; + + // $deploymentQueueId = collect($tags)->first(function ($tag) { + // return str_contains($tag, 'App\Models\ApplicationDeploymentQueue'); + // }); + // $deploymentQueueId = explode(':', $deploymentQueueId)[1]; + // $deploymentQueue = ApplicationDeploymentQueue::find($deploymentQueueId); + // $deploymentQueue->update([ + // 'horizon_job_status' => 'reserved', + // ]); + // } + // }); + } } diff --git a/app/Repositories/CustomJobRepository.php b/app/Repositories/CustomJobRepository.php index ef492e3ba4..3d67cdd1a9 100644 --- a/app/Repositories/CustomJobRepository.php +++ b/app/Repositories/CustomJobRepository.php @@ -68,4 +68,14 @@ public function getLongRunningJobs(int $seconds): Collection return $jobs; } + + public function getQueues(): array + { + $queues = $this->connection()->keys('queue:*'); + $queues = array_map(function ($queue) { + return explode(':', $queue)[2]; + }, $queues); + + return $queues; + } } diff --git a/database/migrations/2025_01_10_135244_add_horizon_job_details_to_queue.php b/database/migrations/2025_01_10_135244_add_horizon_job_details_to_queue.php new file mode 100644 index 0000000000..e6d4693a79 --- /dev/null +++ b/database/migrations/2025_01_10_135244_add_horizon_job_details_to_queue.php @@ -0,0 +1,30 @@ +string('horizon_job_status')->nullable(); + $table->string('horizon_job_worker')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('application_deployment_queues', function (Blueprint $table) { + $table->dropColumn('horizon_job_status'); + $table->dropColumn('horizon_job_worker'); + }); + } +};