Schedule::Advisory - An advisory job scheduler, where each job has a specific run frequency, or interval


This module implements a scheduler for a set of jobs, where each job has a given run frequency or period - i.e. it should run once every so-many seconds. This module can determine which job should run next, and tells the caller which job it has chosen and how long (if at all) the caller needs to wait before starting the job. Note that this module does not sleep() for you, or invoke the job itself - those tasks are left to the caller, because the caller knows how it should best invoke a job (e.g. dispatch table, conditional branch, fork a worker process, ...), and if there are other delays to be accounted for before starting the job. This is why it's an "advisory" scheduler - it doesn't enforce a schedule itself.

See "ALGORITHM" for a description of how the scheduler chooses jobs.

You may add and remove jobs at any time. Each job has a unique ID string which is used to refer to the job. You may alter the run frequency at any time. You can also retrieve a list of all job IDs in the object, and timing information for each.

The module also has a facility for spreading jobs out so that they don't all get scheduled at once, which is especially relevant if you have many jobs with the same period. The module Set::Partition::SimilarValues is used, if available, to help this facility generally work better.

You may optionally store some "userdata" against each job. This userdata may be any single value (a string, number, hash reference, array reference, etc.) and can hold any data associated with the job. You may wish to use this facility if the caller doesn't have access to data required to complete the job. Userdata can be fetched, updated, or deleted at any time.

High Resolution Time

Although it's not required by this module, it's recommended that you install Time::HiRes on your system. It provides sleep() and time() functions which have higher resolution and hence provide better accuracy for scheduling, although that's especially relevant when the interval between jobs is of the order of seconds instead of hours.

The package global $Schedule::Advisory::FoundTimeHiRes is set to 1 if Time::HiRes was loaded, 0 otherwise.


        use Schedule::Advisory;
                # you may also wish to use Time::HiRes; for a high-resolution sleep()
        my $sched = new Schedule::Advisory();
        $sched->add('foo', 300, { 'colour' => 'red' });
        $sched->add('bar', 320, 'some userdata');
        $sched->add('qux', 3600);
        $sched->update_runperiod('bar', 300);
        my @list_of_ids = $sched->all_jobs;
        my ($lastrun, $nextrun, $period) = $sched->get_job_data('foo');
        my $rv = $sched->get_userdata('foo');
        $sched->update_userdata('foo', { 'colour' => 'blue' });
        while ($some_condition) {
                my ($job_id, $delay, $userdata) = $sched->next_job;
                if ($delay) { sleep($delay); }
                do_something_to_invoke_job( $job_id, $userdata );


new( %options )

Class method. Creates a new object and returns it. See "CONSTRUCTOR OPTIONS" for options which may be supplied.

add( $job_id, $run_period, [ $userdata ] )

Adds a new job to the object. The Job ID may be any string, and must be different from any job ID currently in use in the object. The run period is the desired time interval, in seconds, between successive runs of the job. It must be a positive number. E.g. 60 means that the job should run once a minute.

The userdata is optional, and may be any single value (e.g. a number, a string, a hash reference or array reference).

update_runperiod( $job_id, $run_period )

This method allows you to change the run period of a given job. Internally, the job is updated so that the time of the next scheduled occurrence is simply the time it was last run plus the new run period. The job must already exist.

remove( $job_id )

Removes the given job from the object. The job must already exist.

spread( [ $time ] )

Attempts to spread the jobs through time so that they are not all scheduled to occur at the same time. The module Set::Partition::SimilarValues is loaded if available to divide the set of jobs into clusters of jobs with similar run times for better spread. If the module isn't available then all jobs are considered together in a single cluster. The method uses the current time as the basis for its calculations unless you explicitly pass in a different time.

Note this method updates the internal timing information which can re-order the jobs, so you may wish to call this method only infrequently. See also the AutoSpread constructor option.

next_job( [ $time ] )

Determines which job should run next.

Returns the following data in a list: the chosen job ID, the time to wait until the next execution, and the job's userdata (if any). There must be at least one job already in the object. The method uses the current time as the basis for its calculations unless you explicitly pass in a different time. The time is simply the number of seconds since the epoch. You may wish to pass in the time if you're trying to schedule a number of jobs in advance.

The time to wait is 0 if the job should have already been started (i.e. its next occurrence was before the reference time), or a positive number of seconds if there is time to wait before starting the job. Note: as mentioned above ("DESCRIPTION") this module doesn't sleep so you are expected to act on the recommended delay.


Returns a list of all job IDs in the object.

get_job_data( $job_id )

Returns a list of the following information: last run time, next run time, run period. The job must already exist.

get_userdata( $job_id )

Returns the userdata, if any, for the given job ID. The job must already exist.

update_userdata( $job_id, $userdata )

Update the userdata for the given job with the supplied value (the value may even be undef). The job must already exist.

delete_userdata( $job_id )

Delete the userdata for the given job. The job must already exist.



If set to 1 then spread() will be called automatically after add(), remove() and update_runperiod() so that jobs stay spread out in time.


When a job is added it is given a "last run time" of 0. When next_job() is called this module orders all jobs by their next scheduled time. The job with the earliest time is chosen - i.e. the most overdue job or the next scheduled job. If there is a tie then the job that was least recently chosen, or least recently added to the object using the add() method, is chosen this time.

If the job is due to occur in the future then the routine works out the delay required from the current time (or if you passed in an explicit time, it uses that), otherwise a delay of zero is returned.


Time::HiRes, Set::Partition::SimilarValues


Copyright 2005 P Kent

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

$Revision: 1.2 $