Note: You are currently viewing documentation for Moodle 3.8. Up-to-date documentation for the latest stable version of Moodle may be available here: Scheduled Tasks Proposal.

Development talk:Scheduled Tasks Proposal: Difference between revisions

From MoodleDocs
No edit summary
Line 215: Line 215:


: Looks reasonable. I wonder if taskname would be better than taskcode?--[[User:Tim Hunt|Tim Hunt]] 16:28, 8 March 2011 (UTC)
: Looks reasonable. I wonder if taskname would be better than taskcode?--[[User:Tim Hunt|Tim Hunt]] 16:28, 8 March 2011 (UTC)
Yeah, I started with taskname, then thought taskcode might be better.  I don't mind either way.

Revision as of 16:38, 8 March 2011

Pseudo code proposal

This is just a rough idea to provoke thought:--Tim Hunt 11:06, 9 December 2009 (UTC)

<?php function handle_cron($maxprocesstime) {

   $timestart = time();
   $runid = cron_record_starting_processing($timenow);
   while (($timenow = time()) < $timestart + $maxprocesstime) {
       try {
           $task = cron_get_next_task($timenow);
           cron_acquire_lock($task);
           $next = $task->execute($timenow);
           cron_record_task_success($task, $next, $timenow);
           cron_release_lock($task);
       } catch (Exception $e) {
           cron_record_task_failure($task, $timenow, $e);
       }
   }
   cron_record_ending_processing($runid, $timenow);

}

/**

* Instances of this class are rows in the scheduled_tasks table.
*/

abstract class scheduled_task {

   protected $id;
   protected $plugin; // = 'mod_quiz', 'qtype_multichoice'.
   protected $function;
   protected $lastruntime;
   protected $nextscheduledtime;
   protected $priority; // LOW, MEDIUM, HIGH, or, a number like nice 19, or something.
   protected abstract function get_start_end_times($timenow);
   protected abstract function schedule_next($timenow);
   public function execute($timenow) {
       $file = get_plugin_dir($this->plugin) . '/cron.php';
       if (!is_readable($file)) {
           throw new cron_exception('required file does not exist.');
       }
       if (include_once($file)) {
           throw new cron_exception('required file could not be included.');
       }
       list($start, $end) = next_times($timenow);
       $this->$function($start, $end);
       return $this->schedule_next();
   }

}

class one_off_task extends scheduled_task {

   protected abstract function get_start_end_times($timenow) {
       return array(null, $timenow);
   }
   protected abstract function schedule_next($timenow) {
       return null;
   }

}

class catchup_task extends scheduled_task {

   protected $desiredinterval; // seconds
   protected abstract function get_start_end_times($timenow) {
       return array($lastruntime, $timenow);
   }
   protected abstract function schedule_next($timenow) {
       return $timenow + $desiredinterval;
   }

}

class every_day_task extends scheduled_task {

   protected $swtichovertime; // Seconds after midnight.
   protected $runtime; // Seconds after midnight.
   // For now this code assumes $runtime > $swtichovertime, but that is just me
   // being lazy. It can be fixed.
   protected abstract function get_start_end_times($timenow) {
       $midnight = get_midnight_before($this->nextscheduledtime);
       $previousmidnight = get_previous_midnight($midnight);
       $starttime = $previousmidnight + $swtichovertime;
       $endtime = $midnight + $swtichovertime;
       return array($starttime, $endtime);
   }
   protected abstract function schedule_next($timenow) {
       $midnight = get_midnight_before($this->nextscheduledtime);
       $nextmidnight = get_following_midnight($midnight);
       return $nextmidnight + $this->runtime;
   }

}

class weekly_task extends scheduled_task {

   // ...

}

/* Approximate database tables.

scheduled_tasks

   id AUTOINCREMENT
   type varchar
   plugin varchar
   function varchar UNIQUE
   lastruntime int 
   nextscheduledtime int
   priority int
   custom1 int
   custom2 int 
   customextra text

scheduled_task_locks

   id int auto
   function fk unique
   locktime int
  • /

Proposed database changes

We will be implementing a scheduled tasks system in our product, but we need a couple of extra features. In the interest of interoperability and not reinventing cron too many times, here are the features that we need, and the database changes that would be required:

  • plugins need to define multiple cron jobs on-the-fly (one of our plugins will allow the user to schedule several tasks with different schedules), and allow plugins to modify those tasks
  • ability to specify that a task should run n times, or until [date]

For the first requirement, I'd like to add a taskcode field to the DB, to allow plugins to identify the different tasks that it creates. The callfunction would then be called with taskcode as an argument.

For the second requirement, I think that we can roll the onceoff_tasks table into the main tasks table, and just add extra fields for number of times remaining and/or end date. So onceoff tasks would just be a task with the number of runs remaining set to 1, nextruntime set to the time that it should run, and the cron fields set to (empty? or "*"?). With the taskcode field, we wouldn't need the customdata field -- the task can use the taskcode field to identify the task, and load customdata from its own table.

Here is my proposed table, with changes highlighted:

scheduled_tasks:

Field Datatype Comment
id integer sequence
plugintype varchar(50) plugintype - should match the path-style declarations in get_plugin_types (eg question/type, not qtype). Will be null for core tasks.
pluginname varchar(50) name of the plugin. Will be null for core tasks.
taskcode (new) varchar(50) a plugin-specific identifier for the task, used to differentiate between multiple tasks for the same plugin
callfunction varchar(200) (unique) the function to call. Must be unique, as it will be used for the locking.
lastruntime int(10) unix timestamp
nextruntime int(10) unix timestamp
blocking int(1) 0 or 1 - whether this task, when running, blocks everything else from running.
minute varchar(25)
hour varchar(25)
day varchar(25)
month varchar(25)
dayofweek varchar(25)
runsremaining (new) integer Number of times that the task will run
enddate (new) integer Task will not run after this date
customised integer(1) 0 or 1 - whether this time differs from what is in code

Since we can now have multiple tasks with the same callfunction, we can't enforce that it be unique. So we can either use (taskcode,callfunction) or (plugintype,pluginname,taskcode) as the key for locking.

Also, do we need a field to specify which file the callfunction is in?

--Hubert Chathi 14:45, 8 March 2011 (UTC)

Looks reasonable. I wonder if taskname would be better than taskcode?--Tim Hunt 16:28, 8 March 2011 (UTC)

Yeah, I started with taskname, then thought taskcode might be better. I don't mind either way.