Hi! Thank you for this very fun assignment. I enjoyed working on it a lot and was an interesting thought experiment when we consider doing something outside the typical workflow like using Laravel's built-in queueing system.
I created this project to do the following, as requested:
- I created a file
scripts/run-job.phpto instantiate the class provided and run the specified class method with the supplied parameters in the background of the current operation system. - Windows and Linux-based operating systems are both supported.
- Create a helper task
runBackgroundJobthat calls thescripts/run-job.phpscript in a clean, accessible way.
-
Open
config/background-jobs.phpand you'll see the following configurations:php_binary: I used Herd to set the project up, and the FPM process and the command line uses different PHP binaries, so please provide the full path to your local PHP binary here that you get fromwhich php.allowed_jobs: Here you whitelist all the classes and associated class methods are are allowed to be run with this background job code.retry_interval: Specify how many minutes before a failed job can be retried again.max_retries: The maximum number of retries before a job is marked as failed.
-
Follow the steps below to install the package:
- Run
composer install. - Publish the configuration file using
php artisan vendor:publish --tag=background-jobs-config. - Ensure the
scripts/run-job.phpfile is executable by runningchmod +x scripts/run-job.php. - Run
php artisan migrateto create the required database tables.
- Run
Running the runBackgroundJob function is extremely simple. It's available on a global scope and you simply call it from wherever you need to queue a asynchronous background job.
runBackgroundJob(
string $className,
string $methodName,
array $parameters = [],
array $delay = []
);- $className: The fully qualified class name of the job to be executed.
- $methodName: The method within the class to be called.
- $parameters: (Optional) An array of parameters to pass to the method.
- $delay: (Optional) Delay execution of the job in second.
- $priority: (Optional) Priority of the job, integer.
runBackgroundJob(
App\Jobs\ExampleJob::class,
'execute',
['param1', 'param2'],
60
);Below, an example I used the whole time with a class I created to test this:
runBackgroundJob(
'App\Services\UserSeederService',
'seedUsers',
['10'],
60
);You can also run the script directly from the terminal. Example:
<php-binary-path> '<easypeasyfluent-project-path>/scripts/run-job.php' 'App\Services\UserSeederService' seedUsers 2 60From config/background-jobs.php you'll find a globally accesible config value:
config('background-jobs.retry_interval');This values specifies, in minutes, when to schedule the next retry of a failed job. Minutes was chosen to adhere to the smallest timeframe you can schedule cron jobs on, in case background tasks was eventually moved to a cron schedule.
Delay of execution is supported as an optional 4th parameter when calling the helper function runBackgroundJob(). This delays execution to only happen when the time is past next_retry_at datetime as stored in the background_jobs database table for the particular record.
Job priority is introduced as an optional 5th parameter of the global helper function runBackgroundJob().
This specifies the order of priority to place a new job in and on successive retrievals of multiple jobs, all jobs will be sorted by priority first so that they run according to priority and not simply by created_at date.
Example to create a new job and set it to priority 2.
runBackgroundJob(
'App\Services\UserSeederService',
'seedUsers',
['10'],
60,
2
);A dashboard has been created where you can see all the background jobs and their statuses.
I created two files for this.
- Livewire component:
app/Livewire/BackgroundJobsDashboard.php - Liveware and blade template:
resources/views/livewire/background-jobs-dashboard.blade.php
I opted to just do this with Livewire as it's nice and easy, however I am perfectly comfortable to have done it with Vue.js or React.js as well.
To view it, please register for a new account by going to /register. After registration you'll be logged in. When logged in you'll see a link on the left sidebar called Background Jobs.
A few important security features were created and implemented.
- Only whitelisted classes and class methods are allowed to be queue for this background job process.
- All inputs are properly sanitized.
- There are class and method validations done on both the class name and method name.
To test, I created a controller that simply seeds some users to the database. If you run the project via Herd, you can access it as follows:
http://easypeasyfluent.test/seed-users?count=21
Of course, just replace the easypeasyfluent.test domain with your local one.
The following logs have been created:
storage/logs/background_jobs.log- contains information about the starting, running and completion of jobs.storage/logs/background_jobs_errors.log- contains all errors encountered during execution of the background job.
I've also added sample populated logs I generated during my testing. It can be found in the folder sample_logs.
- ✅ I'm happy to report that I implemented the call of the background process using Symfony/Process.
- ✅ I implemented try/catch with exception handling (specific and finally general type exceptions) in every place where execution starts and might throw an error.
- ✅ Job delays
- ✅ Job priorities
- ✅ Basic dashboard