Delayed and Background Jobs
Delayed and Background Jobs
One of the more advanced features of Hazaar is its background job scheduler, which is handled by the Warlock Agent. This mechanism is completely transparent and gives developers the ability to execute jobs in the background easily, run them after a delay, or schedule them to run at a specific date and time, all within the context of the current application. The scheduler is very light but powerful, and so does not consume many resources.
Overview
The scheduler itself runs as a single loop within the Warlock Agent process. Jobs are executed in their own child processes so that they won't hang up or kill the main Agent process. Limits have also been placed on parallel execution jobs so that resources will not be consumed out of control, should a rogue job be doing something it shouldn't. Full logging is enabled with statistics gathering making it easy to identify any problems.
Methods
There are currently two methods for scheduling function execution. In both methods, $function is something that the PHP function is_callable() considers to be a callable object. Support for static method references are also supported, such as App\Model\MyModel::doTheThing.
$warlock = new Hazaar\Warlock\Client();
$warlock->delay($seconds, $function, $tag = null, $overwrite = false);This will run the function after the $seconds number of seconds has passed.
Returns the job ID on success, false otherwise.
$warlock = new Hazaar\Warlock\Client();
$warlock->schedule($when, $function, $tag = null, $overwrite = false);This will schedule the function to execute at a specific date/time specified by $when. The schedule method uses strtotime() internally to resolve text times in $when into epoch values. This means that any string times that strtotime() supports can be used, including 'tomorrow 1pm', 'next week', etc.
You can also use epoch integer values.
Warning
Be aware however that there may be configuration differences between the scheduler and your application which may cause timezones to be different. It is suggested that if you are setting an explicit time of execution that you should specify the timezone in the time string as a matter of course. Using the PHP function date('c', time()) will do this.
Returns the job ID on success, false otherwise.
Example Usage
Consider the following example code:
$warlock = new Hazaar\Warlock\Client();
$code = function() {
echo "APPLICATION_PATH = " . APPLICATION_PATH . "\n";
echo "APPLICATION_ENV = " . APPLICATION_ENV . "\n";
};
if($warlock->delay(5, $code)) {
$this->redirect($this->url());
}
throw new \Exception('Unable to execute delayed function');The above is a very simple example of how to use the delay() method to execute a function after a certain period of time has elapsed. We will now step through each line and outline what is happening.
1 - Instantiate the warlock client object
$warlock = new Hazaar\Warlock\Client();This will set up the warlock client object. This object handles all communication between the application and the scheduler. It can optionally also start up a new scheduler instance if one is not already running.
It is possible to run the scheduler from the command line. See the section running the scheduler from the command-line for info.
2 - Create an 'anonymous function' (aka Closure)
In PHP, anonymous functions are implemented using the Closure class. The syntax to create an anonymous function is just the same as most other languages that allow anonymous functions.
$code = function() {
echo "APPLICATION_PATH = " . APPLICATION_PATH . "\n";
echo "APPLICATION_ENV = " . APPLICATION_ENV . "\n";
};The function that we have created simply echoes out the current APPLICATION_PATH and APPLICATION_ENV environment variables. This will prove that the function is being executed in the correct application context. This output will appear in the scheduler log file.
Because the function is executed in the application context, the $this object will refer to the current application object. Making things like this possible:
$appname = $this->config->app['name'];3 - Schedule the code for execution
$warlock->delay(5, $code);In the above code we send the function to the delay() method and tell it to run the function in 5 seconds.
If we wanted to specify the time at which to run the function we could use the schedule() method.
$warlock->schedule('5pm', $code);This will schedule the function to execute at 5pm.
Tags
To help keep track of jobs we have added a feature called tags. Tags are just string values given to jobs to identify them as unique. If you try and add a job with a tag that already exists, then two things could happen:
- Adding the job will fail. The error returned will state that the job could not be added because a job with the specified tag already exists.
- If the $overwrite parameter is set true, then the job will be overwritten with the new function and schedule information.
The benefits of tagging are that you can ensure that a particular function will never attempt to be executed if it already exists.
Running Warlock from the command-line
To run Warlock from the command line do the following from your application path:
$ vendor/bin/warlockWarlock will then start and you should see logging output to your terminal.
Calling static class methods
A new feature in Warlock 2.2 is the ability to call static class methods instead of having to use a closure. Using a static class method works almost exactly the same as using a closure except that the code can be defined anywhere in your project.
For example:
namespace App\Model;
class MyTestClass
{
public static function doTheThing(){
error_log('The thing is done!');
}
}If you need access to the service context (for example, to call trigger() or log()), use a closure or an instance method instead of a static method.
To execute this method in the background you can pass the callable in one of the following ways:
$this->delay(30, ['App\Model\MyTestClass', 'doTheThing']);is the same as
$this->delay(30, 'App\Model\MyTestClass::doTheThing');It's dealer's choice which you use.
Globally Scheduled Jobs
With Warlock 2.2 it is now possible to schedule jobs to execute without a service, and without being triggered by application code. This allows you to set up static method and have it executed on a specified schedule.
Using the above example class MyTestClass, if we wanted this method to run every hour on business days between 9am and 5pm we can simply add the following to the main Warlock config file warlock.php.
<?php
return [
getenv('APPLICATION_ENV') => [
'schedule' => [
[
'when' => '0 9-17 * * 1-5',
'exec' => 'App\\Model\\MyTestClass::handleTestEvent',
],
],
],
];When the scheduled time rolls around, a new Runner process will be started up and the App\Model\MyTestClass::handleTestEvent method will be executed.
Warning
Notice the double backslashes in the class name. This is required in PHP strings as well to ensure the class name is passed correctly.
Without the double backslashes the class name would be parsed incorrectly and the method would not be executed.