├── LICENSE ├── README.md ├── composer.json └── src ├── Console └── WorkCommand.php ├── SansDaemonServiceProvider.php ├── SansDaemonWorker.php └── Traits └── SansDaemonWorkerTrait.php /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Orobo Lucky Ozoka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SansDaemon 2 | 3 | [![Build Status](https://travis-ci.org/orobogenius/sansdaemon.svg?branch=master)](https://travis-ci.org/orobogenius/sansdaemon) 4 | [![Latest Stable Version](https://poser.pugx.org/queueworker/sansdaemon/v/stable)](https://packagist.org/packages/queueworker/sansdaemon) 5 | [![Total Downloads](https://poser.pugx.org/queueworker/sansdaemon/downloads)](https://packagist.org/packages/queueworker/sansdaemon) 6 | [![License](https://poser.pugx.org/queueworker/sansdaemon/license)](https://packagist.org/packages/queueworker/sansdaemon) 7 | ## Introduction 8 | Batch process Laravel Queue without a daemon; Processes all jobs on the queue(s) and exits without running on daemon mode. This is useful in cases where you just want to process jobs on the queue and exit the worker process so they don't pile up in memory. 9 | 10 | ## Installation 11 | 12 | To install the latest version of SansDaemon, simply use composer 13 | 14 | ### Download 15 | 16 | ``` 17 | composer require queueworker/sansdaemon 18 | ``` 19 | 20 | - If your Laravel version is below 5.5, you'll need to add the service provider to your ```config/app.php``` file. 21 | 22 | ```php 23 | Queueworker\SansDaemon\SansDaemonServiceProvider::class, 24 | ``` 25 | 26 | ## Usage 27 | SansDaemon is a console application that extends the functionality of laravel's `WorkCommand` - ```Illuminate\Queue\Console\WorkCommand```. _See_ [Laravel Queue](https://laravel.com/docs/queues) documentation. 28 | 29 | To run the queue worker sans-daemon mode, simply add the ```--sansdaemon``` option to the original laravel queue worker command: 30 | 31 | ``` 32 | php artisan queue:work --sansdaemon 33 | ``` 34 | 35 | ## Argument and Options 36 | Since this package extends laravel's `WorkCommand`, it takes exactly all the arguments and options the original WorkCommand takes with three added options: 37 | 38 | - `--sansdaemon` option tell the worker to process jobs on the queue without running in daemon mode. 39 | - `--jobs` (default: 0, optional) - It allows you to specify the number of jobs to process each time the command runs. The default value `0` means it'll process all available jobs in the queue. 40 | - `--max_exec_time` (default: `ini_get('max_execution_time') - 5s`, optional) - On some webhosts, your scripts will be killed, if it exceeds some amount of time. To prevent this behavior on really full queue, worker will stop after `--max_exec_time`. This is especially useful if you're running this command via your application's route or controller. See [Laravel Documentation](https://laravel.com/docs/artisan#programmatically-executing-commands) on how to run your queue programmatically. 41 | 42 | #### Note on `--max_exec_time` 43 | - `0` (zero) means the worker will run forever, which in this context means until the worker process is done. This is the default behavior when run from CLI. 44 | - This option will not prevent `Maximum execution time exceeded` error, it'll try to avoid it by not running the next job on the queue if the script is reaching its [max_execution_time](http://php.net/manual/en/info.configuration.php#ini.max-execution-time) 45 | 46 | ## Testing 47 | ``` 48 | composer test 49 | ``` 50 | 51 | ## License 52 | 53 | MIT license (MIT) - Check out the [License File](LICENSE) for more. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "queueworker/sansdaemon", 3 | "type": "library", 4 | "description": "Batch process Laravel Queue without a daemon; Processes queue jobs and kills the process", 5 | "keywords": [ 6 | "queue", 7 | "laravel" 8 | ], 9 | "license": "MIT", 10 | "authors": [{ 11 | "name": "Lucky Ozoka", 12 | "email": "orobolucky@gmail.com" 13 | }], 14 | "require": { 15 | "php": "^7.2|^8.0", 16 | "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0" 17 | }, 18 | "require-dev": { 19 | "phpunit/phpunit": "^8", 20 | "mockery/mockery": "^1.1", 21 | "orchestra/testbench": "^4.0" 22 | }, 23 | "autoload": { 24 | "psr-4": { 25 | "Queueworker\\SansDaemon\\": "src" 26 | } 27 | }, 28 | "autoload-dev": { 29 | "psr-4": { 30 | "Queueworker\\SansDaemon\\": "tests" 31 | } 32 | }, 33 | "extra": { 34 | "laravel": { 35 | "providers": [ 36 | "Queueworker\\SansDaemon\\SansDaemonServiceProvider" 37 | ] 38 | } 39 | }, 40 | "scripts": { 41 | "test": "phpunit" 42 | }, 43 | "minimum-stability": "dev", 44 | "prefer-stable": true 45 | } 46 | -------------------------------------------------------------------------------- /src/Console/WorkCommand.php: -------------------------------------------------------------------------------- 1 | signature .= '{--sansdaemon : Run the worker without a daemon} 31 | {--jobs=0 : Number of jobs to process before worker exits} 32 | {--max_exec_time='.$maxExecutionTime.' : Maximum seconds to run to prevent error (0 - forever)}'; 33 | 34 | $this->description .= ' or sans-daemon'; 35 | 36 | parent::__construct($worker, $cache); 37 | } 38 | 39 | /** 40 | * Run the worker instance. 41 | * 42 | * @param string $connection 43 | * @param string $queue 44 | * @return array 45 | */ 46 | protected function runWorker($connection, $queue) 47 | { 48 | if ($this->option('sansdaemon')) { 49 | $this->worker->setCache($this->laravel['cache']->driver()); 50 | 51 | return $this->runSansDaemon($connection, $queue); 52 | } 53 | 54 | parent::runWorker($connection, $queue); 55 | } 56 | 57 | /** 58 | * Gather all of the queue worker options as a single object. 59 | * 60 | * @return \Illuminate\Queue\WorkerOptions 61 | */ 62 | protected function gatherWorkerOptions() 63 | { 64 | $options = parent::gatherWorkerOptions(); 65 | $options->jobs = $this->option('jobs'); 66 | $options->maxExecutionTime = intval($this->option('max_exec_time')); 67 | 68 | return $options; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/SansDaemonServiceProvider.php: -------------------------------------------------------------------------------- 1 | configureQueue(); 33 | 34 | $this->registerWorkCommand(); 35 | } 36 | 37 | /** 38 | * Configure the queue. 39 | * 40 | * @return void. 41 | */ 42 | protected function configureQueue() 43 | { 44 | if ($this->app->bound('queue.sansDaemonWorker')) { 45 | return; 46 | } 47 | 48 | $this->app->singleton('queue.sansDaemonWorker', function () { 49 | $isDownForMaintenance = function () { 50 | return $this->app->isDownForMaintenance(); 51 | }; 52 | 53 | return new SansDaemonWorker( 54 | $this->app['queue'], 55 | $this->app['events'], 56 | $this->app[ExceptionHandler::class], 57 | $isDownForMaintenance 58 | ); 59 | }); 60 | } 61 | 62 | /** 63 | * Register the command. 64 | * 65 | * @return void 66 | */ 67 | protected function registerWorkCommand() 68 | { 69 | // We'll go through the list of known supported queue commands and 70 | // extend them if they've been bound to the container. 71 | foreach ($this->supportedBaseCommands as $baseCommand) { 72 | if ($this->app->bound($baseCommand)) { 73 | $this->app->extend($baseCommand, function ($command, Application $app) { 74 | return new WorkCommand($app['queue.sansDaemonWorker'], $app['cache.store']); 75 | }); 76 | 77 | break; 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/SansDaemonWorker.php: -------------------------------------------------------------------------------- 1 | getNextJob($connection, $queue); 20 | } 21 | 22 | /** 23 | * Process the given job. 24 | * 25 | * @param \Illuminate\Contracts\Queue\Job $job 26 | * @param string $connectionName 27 | * @param \Illuminate\Queue\WorkerOptions $options 28 | * @return void 29 | */ 30 | public function runSansDaemonJob($job, $connectionName, WorkerOptions $options) 31 | { 32 | return $this->runJob($job, $connectionName, $options); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Traits/SansDaemonWorkerTrait.php: -------------------------------------------------------------------------------- 1 | processJobs($connection, $queue); 24 | } 25 | 26 | /** 27 | * Process jobs from the queue. 28 | * 29 | * @param string $connectionName 30 | * @param string $queue 31 | * @return void 32 | * 33 | * @throws \Throwable 34 | */ 35 | public function processJobs($connectionName, $queue) 36 | { 37 | foreach (explode(',', $queue) as $queue) { 38 | while ($this->shouldRunNextJob() && ! is_null($job = $this->getNextJob($connectionName, $queue))) { 39 | $this->worker->runSansDaemonJob($job, $connectionName, parent::gatherWorkerOptions()); 40 | 41 | if ($this->option('jobs')) { 42 | $this->jobsProcessed += 1; 43 | } 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * Determine if the next job should be processed. 50 | * 51 | * @return bool 52 | */ 53 | protected function shouldRunNextJob() 54 | { 55 | if ($this->isOverMaxExecutionTime($this->gatherWorkerOptions())) { 56 | return false; 57 | } 58 | 59 | if ($jobs = (int) $this->option('jobs')) { 60 | return $this->jobsProcessed != $jobs; 61 | } 62 | 63 | return true; 64 | } 65 | 66 | /** 67 | * Detect if the worker is running longer than the maximum execution time. 68 | * 69 | * @param \Illuminate\Queue\WorkerOptions $options 70 | * @return bool 71 | */ 72 | protected function isOverMaxExecutionTime($options) 73 | { 74 | if ($options->maxExecutionTime <= 0) { 75 | return false; 76 | } 77 | 78 | $elapsedTime = microtime(true) - LARAVEL_START; 79 | 80 | return $elapsedTime > $options->maxExecutionTime; 81 | } 82 | 83 | /** 84 | * Get the next available job from the given queue. 85 | * 86 | * @param string $connectionName 87 | * @param string $queue 88 | * @return \Illuminate\Contracts\Queue\Job|null 89 | */ 90 | protected function getNextJob($connectionName, $queue) 91 | { 92 | return $this->worker->getNextSansDaemonJob( 93 | $this->getConnection($connectionName), $queue 94 | ); 95 | } 96 | 97 | /** 98 | * Get the queue connection. 99 | * 100 | * @param string|null $name 101 | * @return \Illuminate\Contracts\Queue\Queue 102 | */ 103 | protected function getConnection($name) 104 | { 105 | return $this->worker->getManager()->connection($name); 106 | } 107 | } 108 | --------------------------------------------------------------------------------