<?php namespace Laravel\Horizon\Console; use Illuminate\Console\Command; use Illuminate\Contracts\Cache\Factory as CacheFactory; use Illuminate\Support\Arr; use Illuminate\Support\InteractsWithTime; use Illuminate\Support\Str; use Laravel\Horizon\Contracts\MasterSupervisorRepository; use Laravel\Horizon\MasterSupervisor; use Symfony\Component\Console\Attribute\AsCommand; #[AsCommand(name: 'horizon:terminate')] class TerminateCommand extends Command { use InteractsWithTime; /** * The name and signature of the console command. * * @var string */ protected $signature = 'horizon:terminate {--wait : Wait for all workers to terminate}'; /** * The console command description. * * @var string */ protected $description = 'Terminate the master supervisor so it can be restarted'; /** * Execute the console command. * * @param \Illuminate\Contracts\Cache\Factory $cache * @param \Laravel\Horizon\Contracts\MasterSupervisorRepository $masters * @return void */ public function handle(CacheFactory $cache, MasterSupervisorRepository $masters) { if (config('horizon.fast_termination')) { $cache->forever( 'horizon:terminate:wait', $this->option('wait') ); } $masters = collect($masters->all())->filter(function ($master) { return Str::startsWith($master->name, MasterSupervisor::basename()); })->all(); collect(Arr::pluck($masters, 'pid')) ->whenNotEmpty(fn () => $this->components->info('Sending TERM signal to processes.')) ->whenEmpty(fn () => $this->components->info('No processes to terminate.')) ->each(function ($processId) { $result = true; $this->components->task("Process: $processId", function () use ($processId, &$result) { return $result = posix_kill($processId, SIGTERM); }); if (! $result) { $this->components->error("Failed to kill process: {$processId} (".posix_strerror(posix_get_last_error()).')'); } })->whenNotEmpty(fn () => $this->output->writeln('')); $this->laravel['cache']->forever('illuminate:queue:restart', $this->currentTime()); } }