diff --git a/.github/workflows/deptrac.yml b/.github/workflows/deptrac.yml index ebaf4df..f481772 100644 --- a/.github/workflows/deptrac.yml +++ b/.github/workflows/deptrac.yml @@ -20,4 +20,4 @@ on: jobs: deptrac: - uses: codeigniter4/.github/.github/workflows/deptrac.yml@main + uses: codeigniter4/.github/.github/workflows/deptrac.yml@CI46 diff --git a/LICENSE b/LICENSE index 3cbdd9e..684fc13 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ MIT License Copyright (c) 2023 Michal Sniatala -Copyright (c) 2023 CodeIgniter Foundation +Copyright (c) 2023-2025 CodeIgniter Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/docs/commands.md b/docs/commands.md index 2631459..a03ffa2 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -63,6 +63,7 @@ Allows you to consume jobs from a specific queue. * `-priority` - The priority for the jobs from the queue (comma separated). If not provided explicit, will follow the priorities defined in the config via `$queuePriorities` for the given queue. Disabled by default. * `-tries` - The number of attempts after which the job will be considered as failed. Overrides settings from the Job class. Disabled by default. * `-retry-after` - The number of seconds after which the job is to be restarted in case of failure. Overrides settings from the Job class. Disabled by default. +* `-config` - The alternative config file to use. Default value relies on `config('Queue')`. Use namespace to define an alternative, e.g. `Acme\\Config\\Queue`. * `--stop-when-empty` - Stop when the queue is empty. ##### Example @@ -75,6 +76,10 @@ It will listen for 5 jobs from the `emails` queue and then stop. It will work the same as the previous command but will first consume jobs from the `emails` queue that were added with the `low` priority. + php spark queue:work email -config Acme\\Config\\Queue + +This is how we would use an alternative config file. However, usually, you will not need to specify it. It's recommended to use only a single config file across your application, **but** if you are building a modular system, you can use [Registrars](https://codeigniter.com/user_guide/general/configuration.html#registrars) to update the queue config file from within your module. + ### queue:stop Allows you to stop a specific queue in a safe way. It does this as soon as the job that is running in the queue is completed. @@ -83,6 +88,10 @@ Allows you to stop a specific queue in a safe way. It does this as soon as the j * `queueName` - Name of the queue we will work with. +##### Options + +* `-config` - The alternative config file to use. Default value relies on `config('Queue')`. + ##### Example php spark queue:stop emails @@ -106,6 +115,7 @@ Allows you to view all failed jobs. Also only from a specific queue ##### Options * `-queue` - Queue name. +* `-config` - The alternative config file to use. Default value relies on `config('Queue')`. ##### Example @@ -124,6 +134,7 @@ Allows you to retry failed jobs back to the queue. ##### Options * `-queue` - Queue name. +* `-config` - The alternative config file to use. Default value relies on `config('Queue')`. ##### Example @@ -139,6 +150,10 @@ Allows you to delete the failed job by ID * `id` - ID of the failed job. +##### Options + +* `-config` - The alternative config file to use. Default value relies on `config('Queue')`. + ##### Example php spark queue:forget 123 @@ -151,6 +166,7 @@ Allows you to delete many failed jobs at once. Based on the failed date and queu * `-hours` - Number of hours. * `-queue` - Queue name. +* `-config` - The alternative config file to use. Default value relies on `config('Queue')`. ##### Example diff --git a/src/Commands/QueueClear.php b/src/Commands/QueueClear.php index c57ba24..7928fb8 100644 --- a/src/Commands/QueueClear.php +++ b/src/Commands/QueueClear.php @@ -13,18 +13,12 @@ namespace CodeIgniter\Queue\Commands; -use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; +use CodeIgniter\Queue\Config\Queue as QueueConfig; +use CodeIgniter\Queue\Exceptions\QueueException; -class QueueClear extends BaseCommand +class QueueClear extends QueueCommand { - /** - * The Command's Group - * - * @var string - */ - protected $group = 'Queue'; - /** * The Command's Name * @@ -55,6 +49,15 @@ class QueueClear extends BaseCommand 'queueName' => 'Name of the queue we will work with.', ]; + /** + * The Command's Options + * + * @var array + */ + protected $options = [ + '-config' => 'The alternative config file to use. Default value: relies on config(\'Queue\')', + ]; + /** * Actually execute a command. */ @@ -68,7 +71,16 @@ public function run(array $params) return EXIT_ERROR; } - service('queue')->clear($queue); + try { + /** @var QueueConfig $config */ + $config = $this->handleConfig($params); + } catch (QueueException $e) { + CLI::error($e->getMessage()); + + return EXIT_ERROR; + } + + service('queue', $config)->clear($queue); CLI::print('Queue ', 'yellow'); CLI::print($queue, 'light_yellow'); diff --git a/src/Commands/QueueCommand.php b/src/Commands/QueueCommand.php new file mode 100644 index 0000000..c2147bf --- /dev/null +++ b/src/Commands/QueueCommand.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace CodeIgniter\Queue\Commands; + +use CodeIgniter\CLI\BaseCommand; +use CodeIgniter\CLI\CLI; +use CodeIgniter\Queue\Config\Queue as QueueConfig; +use CodeIgniter\Queue\Exceptions\QueueException; + +abstract class QueueCommand extends BaseCommand +{ + /** + * The Command's Group + * + * @var string + */ + protected $group = 'Queue'; + + /** + * @throws QueueException + */ + protected function handleConfig(array $params) + { + $configName = $params['config'] ?? CLI::getOption('config'); + + if ($configName !== null) { + $config = config($configName); + + if ($config === null) { + throw QueueException::forIncorrectConfigFile(); + } + + return $config; + } + + return config('Queue'); + } + + protected function getConfigHash(QueueConfig $config): string + { + return md5($config::class); + } +} diff --git a/src/Commands/QueueFailed.php b/src/Commands/QueueFailed.php index 9d7d2a1..9681246 100644 --- a/src/Commands/QueueFailed.php +++ b/src/Commands/QueueFailed.php @@ -13,19 +13,12 @@ namespace CodeIgniter\Queue\Commands; -use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; use CodeIgniter\Queue\Config\Queue as QueueConfig; +use CodeIgniter\Queue\Exceptions\QueueException; -class QueueFailed extends BaseCommand +class QueueFailed extends QueueCommand { - /** - * The Command's Group - * - * @var string - */ - protected $group = 'Queue'; - /** * The Command's Name * @@ -53,7 +46,8 @@ class QueueFailed extends BaseCommand * @var array */ protected $options = [ - '-queue' => 'Queue name.', + '-queue' => 'Queue name.', + '-config' => 'The alternative config file to use. Default value: relies on config(\'Queue\')', ]; /** @@ -64,10 +58,16 @@ public function run(array $params) // Read params $queue = $params['queue'] ?? CLI::getOption('queue'); - /** @var QueueConfig $config */ - $config = config('Queue'); + try { + /** @var QueueConfig $config */ + $config = $this->handleConfig($params); + } catch (QueueException $e) { + CLI::error($e->getMessage()); + + return EXIT_ERROR; + } - $results = service('queue')->listFailed($queue); + $results = service('queue', $config)->listFailed($queue); $thead = ['ID', 'Connection', 'Queue', 'Class', 'Failed At']; $tbody = []; diff --git a/src/Commands/QueueFlush.php b/src/Commands/QueueFlush.php index 446dc11..7adddbe 100644 --- a/src/Commands/QueueFlush.php +++ b/src/Commands/QueueFlush.php @@ -13,18 +13,12 @@ namespace CodeIgniter\Queue\Commands; -use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; +use CodeIgniter\Queue\Config\Queue as QueueConfig; +use CodeIgniter\Queue\Exceptions\QueueException; -class QueueFlush extends BaseCommand +class QueueFlush extends QueueCommand { - /** - * The Command's Group - * - * @var string - */ - protected $group = 'Queue'; - /** * The Command's Name * @@ -52,8 +46,9 @@ class QueueFlush extends BaseCommand * @var array */ protected $options = [ - '-hours' => 'Number of hours.', - '-queue' => 'Queue name.', + '-hours' => 'Number of hours.', + '-queue' => 'Queue name.', + '-config' => 'The alternative config file to use. Default value: relies on config(\'Queue\')', ]; /** @@ -65,11 +60,20 @@ public function run(array $params) $hours = $params['hours'] ?? CLI::getOption('hours'); $queue = $params['queue'] ?? CLI::getOption('queue'); + try { + /** @var QueueConfig $config */ + $config = $this->handleConfig($params); + } catch (QueueException $e) { + CLI::error($e->getMessage()); + + return EXIT_ERROR; + } + if ($hours !== null) { $hours = (int) $hours; } - service('queue')->flush($hours, $queue); + service('queue', $config)->flush($hours, $queue); if ($hours === null) { CLI::write(sprintf('All failed jobs has been removed from the queue %s', $queue), 'green'); diff --git a/src/Commands/QueueForget.php b/src/Commands/QueueForget.php index 9f930fb..031a23c 100644 --- a/src/Commands/QueueForget.php +++ b/src/Commands/QueueForget.php @@ -13,18 +13,12 @@ namespace CodeIgniter\Queue\Commands; -use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; +use CodeIgniter\Queue\Config\Queue as QueueConfig; +use CodeIgniter\Queue\Exceptions\QueueException; -class QueueForget extends BaseCommand +class QueueForget extends QueueCommand { - /** - * The Command's Group - * - * @var string - */ - protected $group = 'Queue'; - /** * The Command's Name * @@ -55,6 +49,15 @@ class QueueForget extends BaseCommand 'id' => 'ID of the failed job.', ]; + /** + * The Command's Options + * + * @var array + */ + protected $options = [ + '-config' => 'The alternative config file to use. Default value: relies on config(\'Queue\')', + ]; + /** * Actually execute a command. */ @@ -68,7 +71,16 @@ public function run(array $params) return EXIT_ERROR; } - if (service('queue')->forget((int) $id)) { + try { + /** @var QueueConfig $config */ + $config = $this->handleConfig($params); + } catch (QueueException $e) { + CLI::error($e->getMessage()); + + return EXIT_ERROR; + } + + if (service('queue', $config)->forget((int) $id)) { CLI::write(sprintf('Failed job with ID %s has been removed.', $id), 'green'); } else { CLI::write(sprintf('Could not find the failed job with ID %s', $id), 'red'); diff --git a/src/Commands/QueuePublish.php b/src/Commands/QueuePublish.php index 6e4e7bb..f456f13 100644 --- a/src/Commands/QueuePublish.php +++ b/src/Commands/QueuePublish.php @@ -13,14 +13,12 @@ namespace CodeIgniter\Queue\Commands; -use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; use CodeIgniter\Publisher\Publisher; use Throwable; -class QueuePublish extends BaseCommand +class QueuePublish extends QueueCommand { - protected $group = 'Queue'; protected $name = 'queue:publish'; protected $description = 'Publish Queue config file into the current application.'; diff --git a/src/Commands/QueueRetry.php b/src/Commands/QueueRetry.php index c269d53..d34308e 100644 --- a/src/Commands/QueueRetry.php +++ b/src/Commands/QueueRetry.php @@ -13,18 +13,12 @@ namespace CodeIgniter\Queue\Commands; -use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; +use CodeIgniter\Queue\Config\Queue as QueueConfig; +use CodeIgniter\Queue\Exceptions\QueueException; -class QueueRetry extends BaseCommand +class QueueRetry extends QueueCommand { - /** - * The Command's Group - * - * @var string - */ - protected $group = 'Queue'; - /** * The Command's Name * @@ -61,7 +55,8 @@ class QueueRetry extends BaseCommand * @var array */ protected $options = [ - '-queue' => 'Queue name.', + '-queue' => 'Queue name.', + '-config' => 'The alternative config file to use. Default value: relies on config(\'Queue\')', ]; /** @@ -77,11 +72,20 @@ public function run(array $params) return EXIT_ERROR; } + try { + /** @var QueueConfig $config */ + $config = $this->handleConfig($params); + } catch (QueueException $e) { + CLI::error($e->getMessage()); + + return EXIT_ERROR; + } + $id = $id === 'all' ? null : (int) $id; $queue = $params['queue'] ?? CLI::getOption('queue'); - $count = service('queue')->retry($id, $queue); + $count = service('queue', $config)->retry($id, $queue); if ($count === 0) { CLI::write(sprintf('No failed jobs has been restored to the queue %s', $queue), 'red'); diff --git a/src/Commands/QueueStop.php b/src/Commands/QueueStop.php index 219db56..6cc0279 100644 --- a/src/Commands/QueueStop.php +++ b/src/Commands/QueueStop.php @@ -13,18 +13,12 @@ namespace CodeIgniter\Queue\Commands; -use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; +use CodeIgniter\Queue\Config\Queue as QueueConfig; +use CodeIgniter\Queue\Exceptions\QueueException; -class QueueStop extends BaseCommand +class QueueStop extends QueueCommand { - /** - * The Command's Group - * - * @var string - */ - protected $group = 'Queue'; - /** * The Command's Name * @@ -61,6 +55,7 @@ class QueueStop extends BaseCommand * @var array */ protected $options = [ + '-config' => 'The alternative config file to use. Default value: relies on config(\'Queue\')', ]; /** @@ -76,8 +71,19 @@ public function run(array $params) return EXIT_ERROR; } + try { + /** @var QueueConfig $config */ + $config = $this->handleConfig($params); + } catch (QueueException $e) { + CLI::error($e->getMessage()); + + return EXIT_ERROR; + } + + $configHash = $this->getConfigHash($config); + $startTime = microtime(true); - $cacheName = sprintf('queue-%s-stop', $queue); + $cacheName = sprintf('queue-%s-%s-stop', $queue, $configHash); cache()->save($cacheName, $startTime, MINUTE * 10); diff --git a/src/Commands/QueueWork.php b/src/Commands/QueueWork.php index 6a4d4fb..f3aa138 100644 --- a/src/Commands/QueueWork.php +++ b/src/Commands/QueueWork.php @@ -13,22 +13,15 @@ namespace CodeIgniter\Queue\Commands; -use CodeIgniter\CLI\BaseCommand; use CodeIgniter\CLI\CLI; use CodeIgniter\Queue\Config\Queue as QueueConfig; use CodeIgniter\Queue\Entities\QueueJob; +use CodeIgniter\Queue\Exceptions\QueueException; use Exception; use Throwable; -class QueueWork extends BaseCommand +class QueueWork extends QueueCommand { - /** - * The Command's Group - * - * @var string - */ - protected $group = 'Queue'; - /** * The Command's Name * @@ -73,6 +66,7 @@ class QueueWork extends BaseCommand '-priority' => 'The priority for the jobs from the queue (comma separated). If not provided explicit, will follow the priorities defined in the config via $queuePriorities for the given queue. Disabled by default.', '-tries' => 'The number of attempts after which the job will be considered as failed. Overrides settings from the Job class. Disabled by default.', '-retry-after' => 'The number of seconds after which the job is to be restarted in case of failure. Overrides settings from the Job class. Disabled by default.', + '-config' => 'The alternative config file to use. Default value relies on config(\'Queue\')', '--stop-when-empty' => 'Stop when the queue is empty.', ]; @@ -85,8 +79,16 @@ public function run(array $params) { set_time_limit(0); - /** @var QueueConfig $config */ - $config = config('Queue'); + try { + /** @var QueueConfig $config */ + $config = $this->handleConfig($params); + } catch (QueueException $e) { + CLI::error($e->getMessage()); + + return EXIT_ERROR; + } + + $configHash = $this->getConfigHash($config); $stopWhenEmpty = false; $waiting = false; @@ -136,7 +138,7 @@ public function run(array $params) $priority = array_map('trim', explode(',', (string) $priority)); while (true) { - $work = service('queue')->pop($queue, $priority); + $work = service('queue', $config)->pop($queue, $priority); if ($work === null) { if ($stopWhenEmpty) { @@ -156,7 +158,7 @@ public function run(array $params) return EXIT_SUCCESS; } - if ($this->checkStop($queue, $startTime)) { + if ($this->checkStop($queue, $configHash, $startTime)) { return EXIT_SUCCESS; } @@ -178,7 +180,7 @@ public function run(array $params) return EXIT_SUCCESS; } - if ($this->checkStop($queue, $startTime)) { + if ($this->checkStop($queue, $configHash, $startTime)) { return EXIT_SUCCESS; } @@ -244,7 +246,7 @@ private function handleWork(QueueJob $work, QueueConfig $config, ?int $tries, ?i $job->process(); // Mark as done - service('queue')->done($work, $config->keepDoneJobs); + service('queue', $config)->done($work, $config->keepDoneJobs); CLI::write('The processing of this job was successful', 'green'); } catch (Throwable $err) { @@ -295,9 +297,9 @@ private function checkMemory(int $memory): bool return false; } - private function checkStop(string $queue, float $startTime): bool + private function checkStop(string $queue, string $configHash, float $startTime): bool { - $time = cache()->get(sprintf('queue-%s-stop', $queue)); + $time = cache()->get(sprintf('queue-%s-%s-stop', $queue, $configHash)); if ($time === null) { return false; diff --git a/src/Exceptions/QueueException.php b/src/Exceptions/QueueException.php index 542de45..443ce76 100644 --- a/src/Exceptions/QueueException.php +++ b/src/Exceptions/QueueException.php @@ -56,4 +56,9 @@ public static function forIncorrectDelayValue(): static { return new self(lang('Queue.incorrectDelayValue')); } + + public static function forIncorrectConfigFile(): static + { + return new self(lang('Queue.incorrectConfigFile')); + } } diff --git a/src/Language/en/Queue.php b/src/Language/en/Queue.php index 2336197..af9e85f 100644 --- a/src/Language/en/Queue.php +++ b/src/Language/en/Queue.php @@ -25,4 +25,5 @@ 'tooLongPriorityName' => 'The priority name is too long. It should be no longer than 64 letters.', 'incorrectQueuePriority' => 'This queue has incorrectly defined priority: "{0}" for the queue: "{1}".', 'incorrectDelayValue' => 'The number of seconds of delay must be a positive integer.', + 'incorrectConfigFile' => 'The specified config file does not exist.', ]; diff --git a/tests/Commands/QueueStopTest.php b/tests/Commands/QueueStopTest.php index 94e2631..dbb0ea2 100644 --- a/tests/Commands/QueueStopTest.php +++ b/tests/Commands/QueueStopTest.php @@ -46,4 +46,30 @@ public function testRun(): void $this->assertSame('Queue will be stopped after the current job finish', $output); } + + public function testRunWithValidConfig(): void + { + CITestStreamFilter::registration(); + CITestStreamFilter::addOutputFilter(); + + $this->assertNotFalse(command('queue:stop test -config Queue')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeOutputFilter(); + + $this->assertSame('Queue will be stopped after the current job finish', $output); + } + + public function testRunWithInvalidConfig(): void + { + CITestStreamFilter::registration(); + CITestStreamFilter::addErrorFilter(); + + $this->assertNotFalse(command('queue:stop test -config Acme\\Config\\Queue')); + $output = $this->parseOutput(CITestStreamFilter::$buffer); + + CITestStreamFilter::removeErrorFilter(); + + $this->assertStringContainsString('The specified config file does not exist.', $output); + } }