Task API: Difference between revisions
David Mudrak (talk | contribs) m (Text replacement - "</code>" to "</syntaxhighlight>") |
(Added weekday example) |
||
Line 8: | Line 8: | ||
{{Moodle 2.7}} | {{Moodle 2.7}} | ||
== Tasks == | == Tasks == | ||
A task is a unit of work that needs to be done later. Good uses for tasks: | A task is a unit of work that needs to be done later. Good uses for tasks: | ||
* Run a slow operation in the background | * Run a slow operation in the background | ||
* Run a maintenance task on a regular schedule | * Run a maintenance task on a regular schedule | ||
In general any operation that takes more than a few seconds should be a candidate for a task. | In general any operation that takes more than a few seconds should be a candidate for a task. | ||
== Benefits == | == Benefits == | ||
* Better user experience (give them feedback immediately, that their task has been queued) | * Better user experience (give them feedback immediately, that their task has been queued) | ||
* Prevent browser timeouts | * Prevent browser timeouts | ||
Line 23: | Line 18: | ||
* Failed tasks will be retried | * Failed tasks will be retried | ||
* A better user interface will prevent users queuing multiple tasks, because they thought it had "got stuck". | * A better user interface will prevent users queuing multiple tasks, because they thought it had "got stuck". | ||
== Types of task == | == Types of task == | ||
=== Scheduled tasks === | === Scheduled tasks === | ||
Scheduled tasks are tasks that will run on a regular schedule. A default schedule can be set, but admins have the ability to change the default schedule if required. Note: Tasks will only run as often as cron is run in Moodle. In 2.7 it is recommended to run cron once per minute to get the benefit from the new task scheduling (don't worry - it will do much less work each time it runs). | Scheduled tasks are tasks that will run on a regular schedule. A default schedule can be set, but admins have the ability to change the default schedule if required. Note: Tasks will only run as often as cron is run in Moodle. In 2.7 it is recommended to run cron once per minute to get the benefit from the new task scheduling (don't worry - it will do much less work each time it runs). | ||
=== Adhoc tasks === | === Adhoc tasks === | ||
Adhoc tasks are for when you need to queue something to run in the background either immediately where they would be executed as soon as possible, or as a once off task at some future point in time. Adhoc tasks can contain custom data, specific to this specific instance of the task. | Adhoc tasks are for when you need to queue something to run in the background either immediately where they would be executed as soon as possible, or as a once off task at some future point in time. Adhoc tasks can contain custom data, specific to this specific instance of the task. | ||
== Usage == | == Usage == | ||
=== Scheduled task usage === | === Scheduled task usage === | ||
Scheduled tasks are created by subclassing \core\task\scheduled_task. They also require an entry in "db/tasks.php" for your plugin. | Scheduled tasks are created by subclassing \core\task\scheduled_task. They also require an entry in "db/tasks.php" for your plugin. | ||
1. Create a subclass of \core\task\scheduled_task that contains your code to run in a schedule (within "<pluginroot>/classes/task/cut_my_toe_nails.php" for the autoloading mechanism to pick up). | 1. Create a subclass of \core\task\scheduled_task that contains your code to run in a schedule (within "<pluginroot>/classes/task/cut_my_toe_nails.php" for the autoloading mechanism to pick up). | ||
<syntaxhighlight lang="php"> | <syntaxhighlight lang="php"> | ||
namespace mod_hygene\task; | namespace mod_hygene\task; | ||
Line 82: | Line 69: | ||
]; | ]; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
The fields required in the db/tasks.php file are: | The fields required in the db/tasks.php file are: | ||
* classname - the fully namespaced classname of your task. This needs to comply with the autoloading rules. | * classname - the fully namespaced classname of your task. This needs to comply with the autoloading rules. | ||
* blocking - if this is set to 1, no other scheduled task will run at the same time as this task. Do not set this to 1 unless you really need it as it will impact the performance of the task queue. | * blocking - if this is set to 1, no other scheduled task will run at the same time as this task. Do not set this to 1 unless you really need it as it will impact the performance of the task queue. | ||
* minute, hour, day, dayofweek, month - This is the default schedule for running the task. The syntax matches the syntax of unix cron. Starting with Moodle 2.8, the minute and hour values accept a special 'R' syntax which causes a random value to be set in the database at install time (useful to avoid overloading web services). | * minute, hour, day, dayofweek, month - This is the default schedule for running the task. The syntax matches the syntax of unix cron. Starting with Moodle 2.8, the minute and hour values accept a special 'R' syntax which causes a random value to be set in the database at install time (useful to avoid overloading web services). | ||
3. Increase the version number in version.php for your plugin and visit the Site "Administration -> Notifications" page to install the new task. | 3. Increase the version number in version.php for your plugin and visit the Site "Administration -> Notifications" page to install the new task. | ||
4. (Optional - since 2.8) Run this task even when the plugin is disabled. In rare cases, you may want the scheduled tasks for a plugin to run, even when the plugin is disabled. Some of the enrolment plugins do this to clean up data. If this is the case, the scheduled task must override the | 4. (Optional - since 2.8) Run this task even when the plugin is disabled. In rare cases, you may want the scheduled tasks for a plugin to run, even when the plugin is disabled. Some of the enrolment plugins do this to clean up data. If this is the case, the scheduled task must override the "get_run_if_component_disabled()" method and return true instead of false. If they do not do this, the scheduled task will not be run while the plugin is disabled. | ||
When called from the command line for testing purposes errors can be hidden and a misleading error about locks can be displayed. To get at the details of an error Moodle 3.2 supports a showdebugging option that will show the actual errors. Read about it here | When called from the command line for testing purposes errors can be hidden and a misleading error about locks can be displayed. To get at the details of an error Moodle 3.2 supports a showdebugging option that will show the actual errors. Read about it here | ||
https://docs.moodle.org/32/en/Administration_via_command_line#Scheduled_tasks | https://docs.moodle.org/32/en/Administration_via_command_line#Scheduled_tasks | ||
Line 108: | Line 92: | ||
** "6" - Every Saturday | ** "6" - Every Saturday | ||
** "1,5" - Every Monday and Friday | ** "1,5" - Every Monday and Friday | ||
** "1-5" - Every weekday | |||
* hour - Hour field for task schedule. | * hour - Hour field for task schedule. | ||
** "*" - Every hour | ** "*" - Every hour | ||
Line 125: | Line 110: | ||
** "1" - Every January | ** "1" - Every January | ||
** "1,5" - Every January and May | ** "1,5" - Every January and May | ||
=== Adhoc task usage === | === Adhoc task usage === | ||
This is even easier than scheduled tasks. | This is even easier than scheduled tasks. | ||
1. Create a subclass of \core\task\adhoc_task that contains your code to run in the background. | 1. Create a subclass of \core\task\adhoc_task that contains your code to run in the background. | ||
<syntaxhighlight lang="php"> | <syntaxhighlight lang="php"> | ||
class take_over_the_world extends \core\task\adhoc_task { | class take_over_the_world extends \core\task\adhoc_task { | ||
Line 144: | Line 126: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
2. Create an instance of the task and queue it (adding custom data if required). | 2. Create an instance of the task and queue it (adding custom data if required). | ||
<syntaxhighlight lang="php"> | <syntaxhighlight lang="php"> | ||
// create the instance | // create the instance | ||
Line 162: | Line 142: | ||
// profit | // profit | ||
</syntaxhighlight> | </syntaxhighlight> | ||
3. There is no 3. | 3. There is no 3. | ||
By default, your task will run as admin. If you would like it to run as a specific user, call: | By default, your task will run as admin. If you would like it to run as a specific user, call: | ||
<syntaxhighlight lang="php"> | <syntaxhighlight lang="php"> | ||
$task->set_userid($user->id); | $task->set_userid($user->id); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==== Set a task to run at a future time ==== | ==== Set a task to run at a future time ==== | ||
<syntaxhighlight lang="php"> | <syntaxhighlight lang="php"> | ||
$task = new my_future_task(); | $task = new my_future_task(); | ||
Line 178: | Line 154: | ||
\core\task\manager::queue_adhoc_task($task); | \core\task\manager::queue_adhoc_task($task); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==== Ignore duplicate adhoc tasks ==== | ==== Ignore duplicate adhoc tasks ==== | ||
{{Moodle 3.3}} | {{Moodle 3.3}} | ||
If you pass true as the second argument $checkforexisting and a task with the same classname, component and customdata is already scheduled then it will not schedule a new task. | If you pass true as the second argument $checkforexisting and a task with the same classname, component and customdata is already scheduled then it will not schedule a new task. | ||
<syntaxhighlight lang="php"> | <syntaxhighlight lang="php"> | ||
\core\task\manager::queue_adhoc_task($task, true); | \core\task\manager::queue_adhoc_task($task, true); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==== Rescheduling an adhoc task ==== | ==== Rescheduling an adhoc task ==== | ||
{{Moodle 3.7}} | {{Moodle 3.7}} | ||
A method to allow queuing or rescheduling of an existing scheduled task was added. This allows an existing task to be updated or queued as required. You can use this to implement "debounce" and its a good pattern do some expensive processing after some action, but the action maybe repeated several times and it keeps pushing back the task processing until there is lull in activity. | A method to allow queuing or rescheduling of an existing scheduled task was added. This allows an existing task to be updated or queued as required. You can use this to implement "debounce" and its a good pattern do some expensive processing after some action, but the action maybe repeated several times and it keeps pushing back the task processing until there is lull in activity. | ||
<syntaxhighlight lang="php"> | <syntaxhighlight lang="php"> | ||
$task = new heavy_debounced_task(); | $task = new heavy_debounced_task(); | ||
Line 200: | Line 170: | ||
\core\task\manager::reschedule_or_queue_adhoc_task($task); | \core\task\manager::reschedule_or_queue_adhoc_task($task); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Failures === | === Failures === | ||
A task, either scheduled or adhoc can sometimes fail. An example would be updating an RSS field when the network is temporarily down. This is handled by the task system automatically - all the failing task needs to do is throw an exception. The task will be retried after 1 minute. If the task keeps failing, the retry algorithm will add more time between each successive attempts up to a max of 24 hours. | A task, either scheduled or adhoc can sometimes fail. An example would be updating an RSS field when the network is temporarily down. This is handled by the task system automatically - all the failing task needs to do is throw an exception. The task will be retried after 1 minute. If the task keeps failing, the retry algorithm will add more time between each successive attempts up to a max of 24 hours. | ||
=== Caches === | === Caches === | ||
There is one special case that needs to be considered with this new system. If a particular scheduled or adhoc task runs for a long time and updates many DB records - particularly something related to enrolment - the next task in the queue may suffer because various API's in moodle use static caching to speed up requests, but assume that the data will not change much between the start and end of the request. In this case, you can force the cron to exit after running a task that has done many DB updates. The next cron will be run in the next minute, and will have all static caches cleared because it's a new process. To do this call \core\task\manager::clear_static_caches(); | There is one special case that needs to be considered with this new system. If a particular scheduled or adhoc task runs for a long time and updates many DB records - particularly something related to enrolment - the next task in the queue may suffer because various API's in moodle use static caching to speed up requests, but assume that the data will not change much between the start and end of the request. In this case, you can force the cron to exit after running a task that has done many DB updates. The next cron will be run in the next minute, and will have all static caches cleared because it's a new process. To do this call \core\task\manager::clear_static_caches(); | ||
=== Security === | === Security === | ||
When scheduling a task to run in the background - or creating a scheduled task, the task will run in the context of the cron user (see "cron_setup_user()"). If you need to perform access checks in your background task, you should pass the userid/context in custom_data and then pass that userid to the access check functions ("require_capability('moodle/course:update', | When scheduling a task to run in the background - or creating a scheduled task, the task will run in the context of the cron user (see "cron_setup_user()"). If you need to perform access checks in your background task, you should pass the userid/context in custom_data and then pass that userid to the access check functions ("require_capability('moodle/course:update', $context, $userid)"). | ||
=== Legacy cron === | === Legacy cron === | ||
The older syntax of cron.php or modname_cron() is still supported - but is not as good as this new API. This is because: | The older syntax of cron.php or modname_cron() is still supported - but is not as good as this new API. This is because: | ||
Line 215: | Line 181: | ||
* the legacy cron functions are fragile - a failure in one cron in one plugin will prevent the cron in other functions from running at all | * the legacy cron functions are fragile - a failure in one cron in one plugin will prevent the cron in other functions from running at all | ||
* the scheduling cannot be changed by admins | * the scheduling cannot be changed by admins | ||
Note that Legacy cron has been deprecated for Moodle 3.5 (MDL-52846) and will be deleted for Moodle 3.9 (MDL-61165). | Note that Legacy cron has been deprecated for Moodle 3.5 (MDL-52846) and will be deleted for Moodle 3.9 (MDL-61165). | ||
=== Generating output === | === Generating output === | ||
Since Moodle 3.5 it is safe to use the [[Output_API]] in cron tasks. Prior to this there may be cases where the Output API has not been initialised. | Since Moodle 3.5 it is safe to use the [[Output_API]] in cron tasks. Prior to this there may be cases where the Output API has not been initialised. | ||
Line 223: | Line 187: | ||
In order to improve debugging information, it is good practice to call ''mtrace'' to log what's going on within a task execution: | In order to improve debugging information, it is good practice to call ''mtrace'' to log what's going on within a task execution: | ||
<syntaxhighlight lang="php"> | <syntaxhighlight lang="php"> | ||
class my_task extends \core\task\scheduled_task { | class my_task extends \core\task\scheduled_task { | ||
Line 237: | Line 200: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== For Admins == | == For Admins == | ||
Admins have a new screen where they can adjust the schedules for any scheduled task. They can also reset any scheduled task to its default schedule. | Admins have a new screen where they can adjust the schedules for any scheduled task. They can also reset any scheduled task to its default schedule. | ||
[[File:task_admin_screenshot.png|Screenshot of admin page]] | [[File:task_admin_screenshot.png|Screenshot of admin page]] | ||
NOTE: You can also run [https://docs.moodle.org/32/en/Administration_via_command_line#Scheduled_tasks Scheduled tasks] via command line, individually. | NOTE: You can also run [https://docs.moodle.org/32/en/Administration_via_command_line#Scheduled_tasks Scheduled tasks] via command line, individually. | ||
== Specification == | == Specification == | ||
The specification for this feature is here: https://docs.moodle.org/dev/Scheduled_Tasks_Proposal | The specification for this feature is here: https://docs.moodle.org/dev/Scheduled_Tasks_Proposal |
Revision as of 22:41, 20 March 2022
Task | |
---|---|
Project state | Integrated |
Tracker issue | MDL-25505 |
Discussion | https://moodle.org/mod/forum/discuss.php?d=229139 |
Assignee | Damyon |
Moodle 2.7
Tasks
A task is a unit of work that needs to be done later. Good uses for tasks:
- Run a slow operation in the background
- Run a maintenance task on a regular schedule
In general any operation that takes more than a few seconds should be a candidate for a task.
Benefits
- Better user experience (give them feedback immediately, that their task has been queued)
- Prevent browser timeouts
- Better performance for clusters (tasks can be run on separate, non-webserving cluster node, tasks can run in parallel)
- Failed tasks will be retried
- A better user interface will prevent users queuing multiple tasks, because they thought it had "got stuck".
Types of task
Scheduled tasks
Scheduled tasks are tasks that will run on a regular schedule. A default schedule can be set, but admins have the ability to change the default schedule if required. Note: Tasks will only run as often as cron is run in Moodle. In 2.7 it is recommended to run cron once per minute to get the benefit from the new task scheduling (don't worry - it will do much less work each time it runs).
Adhoc tasks
Adhoc tasks are for when you need to queue something to run in the background either immediately where they would be executed as soon as possible, or as a once off task at some future point in time. Adhoc tasks can contain custom data, specific to this specific instance of the task.
Usage
Scheduled task usage
Scheduled tasks are created by subclassing \core\task\scheduled_task. They also require an entry in "db/tasks.php" for your plugin.
1. Create a subclass of \core\task\scheduled_task that contains your code to run in a schedule (within "<pluginroot>/classes/task/cut_my_toe_nails.php" for the autoloading mechanism to pick up).
namespace mod_hygene\task;
/**
* An example of a scheduled task.
*/
class cut_my_toe_nails extends \core\task\scheduled_task {
/**
* Return the task's name as shown in admin screens.
*
* @return string
*/
public function get_name() {
return get_string('cutmytoenails', 'mod_hygene');
}
/**
* Execute the task.
*/
public function execute() {
// Apply fungus cream.
// Apply chainsaw.
// Apply olive oil.
}
}
2. Create entry in db/tasks.php for your plugin (then a version bump to your plugin will install the task):
$tasks = [
[
'classname' => 'mod_hygene\task\cut_my_toe_nails',
'blocking' => 0,
'minute' => '30',
'hour' => '17',
'day' => '*',
'month' => '1,7',
'dayofweek' => '0',
],
];
The fields required in the db/tasks.php file are:
- classname - the fully namespaced classname of your task. This needs to comply with the autoloading rules.
- blocking - if this is set to 1, no other scheduled task will run at the same time as this task. Do not set this to 1 unless you really need it as it will impact the performance of the task queue.
- minute, hour, day, dayofweek, month - This is the default schedule for running the task. The syntax matches the syntax of unix cron. Starting with Moodle 2.8, the minute and hour values accept a special 'R' syntax which causes a random value to be set in the database at install time (useful to avoid overloading web services).
3. Increase the version number in version.php for your plugin and visit the Site "Administration -> Notifications" page to install the new task.
4. (Optional - since 2.8) Run this task even when the plugin is disabled. In rare cases, you may want the scheduled tasks for a plugin to run, even when the plugin is disabled. Some of the enrolment plugins do this to clean up data. If this is the case, the scheduled task must override the "get_run_if_component_disabled()" method and return true instead of false. If they do not do this, the scheduled task will not be run while the plugin is disabled.
When called from the command line for testing purposes errors can be hidden and a misleading error about locks can be displayed. To get at the details of an error Moodle 3.2 supports a showdebugging option that will show the actual errors. Read about it here https://docs.moodle.org/32/en/Administration_via_command_line#Scheduled_tasks
Cron syntax examples
- day - Day of month field for task schedule.
- "*" - Every day
- "*/2" - Every 2nd day
- "1" - The first of every month
- "1,15" - The first and fifteenth of every month
- dayofweek - Day of week field for task schedule.
- "*" - Every day
- "0" - Every Sunday
- "6" - Every Saturday
- "1,5" - Every Monday and Friday
- "1-5" - Every weekday
- hour - Hour field for task schedule.
- "*" - Every hour
- "*/2" - Every 2 hours
- "2-10" - Every hour from 2am until 10am (inclusive)
- "2,6,9" - 2am, 6am and 9am
- "R" - A random value between 0 and 23 is chosen at task install time (or when reset to default) [Supported since Moodle 2.8]
- minute - Minute field for task schedule.
- "*" - Every minute
- "*/5" - Every 5 minutes
- "2-10" - Every minute between 2 and 10 minutes past the hour (inclusive)
- "2,6,9" - 2, 6 and 9 minutes past the hour
- "R" - A random value between 0 and 59 is chosen at task install time (or when reset to default) [Supported since Moodle 2.8]
- month - Month field for task schedule.
- "*" - Every month
- "*/2" - Every second month
- "1" - Every January
- "1,5" - Every January and May
Adhoc task usage
This is even easier than scheduled tasks.
1. Create a subclass of \core\task\adhoc_task that contains your code to run in the background.
class take_over_the_world extends \core\task\adhoc_task {
public function execute() {
// gain 100,000,000 friends on facebook.
// crash the stock market.
// run for president.
// Get the custom data.
$data = $this->get_custom_data();
}
}
2. Create an instance of the task and queue it (adding custom data if required).
// create the instance
$domination = new take_over_the_world();
// set blocking if required (it probably isn't)
// $domination->set_blocking(false);
// add custom data
$domination->set_custom_data(array(
'plansfortomorrownight' => 'The same thing we do every night, Pinky!'
));
// queue it
\core\task\manager::queue_adhoc_task($domination);
// profit
3. There is no 3.
By default, your task will run as admin. If you would like it to run as a specific user, call:
$task->set_userid($user->id);
Set a task to run at a future time
$task = new my_future_task();
$task->set_next_run_time($futuretime);
\core\task\manager::queue_adhoc_task($task);
Ignore duplicate adhoc tasks
Moodle 3.3
If you pass true as the second argument $checkforexisting and a task with the same classname, component and customdata is already scheduled then it will not schedule a new task.
\core\task\manager::queue_adhoc_task($task, true);
Rescheduling an adhoc task
Moodle 3.7
A method to allow queuing or rescheduling of an existing scheduled task was added. This allows an existing task to be updated or queued as required. You can use this to implement "debounce" and its a good pattern do some expensive processing after some action, but the action maybe repeated several times and it keeps pushing back the task processing until there is lull in activity.
$task = new heavy_debounced_task();
$task->set_next_run_time(time() + 5 * MINSECS);
\core\task\manager::reschedule_or_queue_adhoc_task($task);
Failures
A task, either scheduled or adhoc can sometimes fail. An example would be updating an RSS field when the network is temporarily down. This is handled by the task system automatically - all the failing task needs to do is throw an exception. The task will be retried after 1 minute. If the task keeps failing, the retry algorithm will add more time between each successive attempts up to a max of 24 hours.
Caches
There is one special case that needs to be considered with this new system. If a particular scheduled or adhoc task runs for a long time and updates many DB records - particularly something related to enrolment - the next task in the queue may suffer because various API's in moodle use static caching to speed up requests, but assume that the data will not change much between the start and end of the request. In this case, you can force the cron to exit after running a task that has done many DB updates. The next cron will be run in the next minute, and will have all static caches cleared because it's a new process. To do this call \core\task\manager::clear_static_caches();
Security
When scheduling a task to run in the background - or creating a scheduled task, the task will run in the context of the cron user (see "cron_setup_user()"). If you need to perform access checks in your background task, you should pass the userid/context in custom_data and then pass that userid to the access check functions ("require_capability('moodle/course:update', $context, $userid)").
Legacy cron
The older syntax of cron.php or modname_cron() is still supported - but is not as good as this new API. This is because:
- the legacy cron functions run serially - a long running cron in one plugin will hold up the other plugins crons
- the legacy cron functions are fragile - a failure in one cron in one plugin will prevent the cron in other functions from running at all
- the scheduling cannot be changed by admins
Note that Legacy cron has been deprecated for Moodle 3.5 (MDL-52846) and will be deleted for Moodle 3.9 (MDL-61165).
Generating output
Since Moodle 3.5 it is safe to use the Output_API in cron tasks. Prior to this there may be cases where the Output API has not been initialised. These issues were addressed in MDL-61800 and were also backported to Moodle 3.4.3, and Moodle 3.3.3.
In order to improve debugging information, it is good practice to call mtrace to log what's going on within a task execution:
class my_task extends \core\task\scheduled_task {
public function execute() {
mtrace("My task started");
// do some work...
mtrace("My task finished");
}
}
For Admins
Admins have a new screen where they can adjust the schedules for any scheduled task. They can also reset any scheduled task to its default schedule.
NOTE: You can also run Scheduled tasks via command line, individually.
Specification
The specification for this feature is here: https://docs.moodle.org/dev/Scheduled_Tasks_Proposal