From b51b696927c8861b0aa2382626be85768ba0c96f Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Tue, 29 Jan 2019 09:17:19 +0800 Subject: [PATCH 1/1] MDL-46881 core: Allow adhoc tasks to be rescheduled --- lib/classes/task/manager.php | 39 +++++++++- lib/tests/adhoc_task_test.php | 108 +++++++++++++++++++++++++++ lib/upgrade.txt | 2 + mod/forum/classes/task/cron_task.php | 2 +- 4 files changed, 148 insertions(+), 3 deletions(-) diff --git a/lib/classes/task/manager.php b/lib/classes/task/manager.php index 59520182142..87fe34287ac 100644 --- a/lib/classes/task/manager.php +++ b/lib/classes/task/manager.php @@ -129,7 +129,18 @@ class manager { * @return bool */ protected static function task_is_scheduled($task) { + return false !== self::get_queued_adhoc_task_record($task); + } + + /** + * Checks if the task with the same classname, component and customdata is already scheduled + * + * @param adhoc_task $task + * @return bool + */ + protected static function get_queued_adhoc_task_record($task) { global $DB; + $record = self::record_from_adhoc_task($task); $params = [$record->classname, $record->component, $record->customdata]; $sql = 'classname = ? AND component = ? AND ' . @@ -139,14 +150,38 @@ class manager { $params[] = $record->userid; $sql .= " AND userid = ? "; } - return $DB->record_exists_select('task_adhoc', $sql, $params); + return $DB->get_record_select('task_adhoc', $sql, $params); + } + + /** + * Schedule a new task, or reschedule an existing adhoc task which has matching data. + * + * Only a task matching the same user, classname, component, and customdata will be rescheduled. + * If these values do not match exactly then a new task is scheduled. + * + * @param \core\task\adhoc_task $task - The new adhoc task information to store. + * @since Moodle 3.7 + */ + public static function reschedule_or_queue_adhoc_task(adhoc_task $task) : void { + global $DB; + + if ($existingrecord = self::get_queued_adhoc_task_record($task)) { + // Only update the next run time if it is explicitly set on the task. + $nextruntime = $task->get_next_run_time(); + if ($nextruntime && ($existingrecord->nextruntime != $nextruntime)) { + $DB->set_field('task_adhoc', 'nextruntime', $nextruntime, ['id' => $existingrecord->id]); + } + } else { + // There is nothing queued yet. Just queue as normal. + self::queue_adhoc_task($task); + } } /** * Queue an adhoc task to run in the background. * * @param \core\task\adhoc_task $task - The new adhoc task information to store. - * @param bool $checkforexisting - If set to true and the task with the same classname, component and customdata + * @param bool $checkforexisting - If set to true and the task with the same user, classname, component and customdata * is already scheduled then it will not schedule a new task. Can be used only for ASAP tasks. * @return boolean - True if the config was saved. */ diff --git a/lib/tests/adhoc_task_test.php b/lib/tests/adhoc_task_test.php index 2b8b7d1e19d..c5d796102f8 100644 --- a/lib/tests/adhoc_task_test.php +++ b/lib/tests/adhoc_task_test.php @@ -150,6 +150,114 @@ class core_adhoc_task_testcase extends advanced_testcase { } } + /** + * Ensure that the reschedule_or_queue_adhoc_task function will schedule a new task if no tasks exist. + */ + public function test_reschedule_or_queue_adhoc_task_no_existing() { + $this->resetAfterTest(true); + + // Schedule adhoc task. + $task = new \core\task\adhoc_test_task(); + $task->set_custom_data(['courseid' => 10]); + \core\task\manager::reschedule_or_queue_adhoc_task($task); + $this->assertEquals(1, count(\core\task\manager::get_adhoc_tasks('core\task\adhoc_test_task'))); + } + + /** + * Ensure that the reschedule_or_queue_adhoc_task function will schedule a new task if a task for the same user does + * not exist. + */ + public function test_reschedule_or_queue_adhoc_task_different_user() { + $this->resetAfterTest(true); + $user = \core_user::get_user_by_username('admin'); + + // Schedule adhoc task. + $task = new \core\task\adhoc_test_task(); + $task->set_custom_data(['courseid' => 10]); + \core\task\manager::reschedule_or_queue_adhoc_task($task); + + // Schedule adhoc task for a different user. + $task = new \core\task\adhoc_test_task(); + $task->set_custom_data(['courseid' => 10]); + $task->set_userid($user->id); + \core\task\manager::reschedule_or_queue_adhoc_task($task); + + $this->assertEquals(2, count(\core\task\manager::get_adhoc_tasks('core\task\adhoc_test_task'))); + } + + /** + * Ensure that the reschedule_or_queue_adhoc_task function will schedule a new task if a task with different custom + * data exists. + */ + public function test_reschedule_or_queue_adhoc_task_different_data() { + $this->resetAfterTest(true); + + // Schedule adhoc task. + $task = new \core\task\adhoc_test_task(); + $task->set_custom_data(['courseid' => 10]); + \core\task\manager::reschedule_or_queue_adhoc_task($task); + + // Schedule adhoc task for a different user. + $task = new \core\task\adhoc_test_task(); + $task->set_custom_data(['courseid' => 11]); + \core\task\manager::reschedule_or_queue_adhoc_task($task); + + $this->assertEquals(2, count(\core\task\manager::get_adhoc_tasks('core\task\adhoc_test_task'))); + } + + /** + * Ensure that the reschedule_or_queue_adhoc_task function will not make any change for matching data if no time was + * specified. + */ + public function test_reschedule_or_queue_adhoc_task_match_no_change() { + $this->resetAfterTest(true); + + // Schedule adhoc task. + $task = new \core\task\adhoc_test_task(); + $task->set_custom_data(['courseid' => 10]); + $task->set_next_run_time(time() + DAYSECS); + \core\task\manager::reschedule_or_queue_adhoc_task($task); + + $before = \core\task\manager::get_adhoc_tasks('core\task\adhoc_test_task'); + + // Schedule the task again but do not specify a time. + $task = new \core\task\adhoc_test_task(); + $task->set_custom_data(['courseid' => 10]); + \core\task\manager::reschedule_or_queue_adhoc_task($task); + + $this->assertEquals(1, count(\core\task\manager::get_adhoc_tasks('core\task\adhoc_test_task'))); + $this->assertEquals($before, \core\task\manager::get_adhoc_tasks('core\task\adhoc_test_task')); + } + + /** + * Ensure that the reschedule_or_queue_adhoc_task function will update the run time if there are planned changes. + */ + public function test_reschedule_or_queue_adhoc_task_match_update_runtime() { + $this->resetAfterTest(true); + $initialruntime = time() + DAYSECS; + $newruntime = time() + WEEKSECS; + + // Schedule adhoc task. + $task = new \core\task\adhoc_test_task(); + $task->set_custom_data(['courseid' => 10]); + $task->set_next_run_time($initialruntime); + \core\task\manager::reschedule_or_queue_adhoc_task($task); + + $before = \core\task\manager::get_adhoc_tasks('core\task\adhoc_test_task'); + + // Schedule the task again. + $task = new \core\task\adhoc_test_task(); + $task->set_custom_data(['courseid' => 10]); + $task->set_next_run_time($newruntime); + \core\task\manager::reschedule_or_queue_adhoc_task($task); + + $tasks = \core\task\manager::get_adhoc_tasks('core\task\adhoc_test_task'); + $this->assertEquals(1, count($tasks)); + $this->assertNotEquals($before, $tasks); + $firsttask = reset($tasks); + $this->assertEquals($newruntime, $firsttask->get_next_run_time()); + } + /** * Test queue_adhoc_task "if not scheduled". */ diff --git a/lib/upgrade.txt b/lib/upgrade.txt index 85225fdfb9d..54e55231053 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -6,6 +6,8 @@ information provided here is intended especially for developers. * The method core_user::is_real_user() now returns false for userid = 0 parameter * 'mform1' dependencies (in themes, js...) will stop working because a randomly generated string has been added to the id attribute on forms to avoid collisions in forms loaded in AJAX requests. +* A new method to allow queueing or rescheduling of an existing scheduled task was added. This allows an existing task + to be updated or queued as required. This new functionality can be found in \core\task\manager::reschedule_or_queue_adhoc_task. === 3.6 === diff --git a/mod/forum/classes/task/cron_task.php b/mod/forum/classes/task/cron_task.php index 74fdfd73230..5b88606c059 100644 --- a/mod/forum/classes/task/cron_task.php +++ b/mod/forum/classes/task/cron_task.php @@ -342,7 +342,7 @@ class cron_task extends \core\task\scheduled_task { $task->set_userid($user->id); $task->set_component('mod_forum'); $task->set_next_run_time($digesttime); - \core\task\manager::queue_adhoc_task($task, true); + \core\task\manager::reschedule_or_queue_adhoc_task($task); $usercounts['digests']++; $send = true; } -- 2.43.0