
=head1  NAME

cPanel::TaskQueue::Scheduler - Priority queue of Tasks to Queue at some time in the future.

=head1 VERSION

This document describes cPanel::TaskQueue::Scheduler version 0.604.

=head1 SYNOPSIS

    use cPanel::TaskQueue;
    use cPanel::TaskQueue::Scheduler;

    my $queue = cPanel::TaskQueue->new( { name => 'tasks', state_dir => '/home/$user/.cpanel/state' } );
    my $sched = cPanel::TaskQueue::Scheduler->new( { name => 'tasks', state_dir => '/home/$user/.cpanel/state' } );

    $sched->schedule_task( 'init_quota', {delay_seconds=>10} );
    $sched->schedule_task( 'edit_quota fred 0', {delay_seconds=>60} );

    # ... some time later ...
    # This processing loop is a it more complicated than the one for just
    # the TaskQueue.
    while (1) {
        eval {
            $sched->process_ready_tasks( $queue );
            if ( $queue->has_work_to_do() ) {
                $queue->process_next_task();
            }
            else {
                my $wait = $sched->seconds_until_next_task();
                next if defined $wait and 0 == $wait;

                $wait = $default_wait if !$wait || $wait > $default_wait;
                sleep $wait;
            }
        };
        Carp::carp "Exception detected: $@" if $@;
    }

=head1  DESCRIPTION

This module provides the ability to schedule tasks for later insertion into a
C<cPanel::TaskQueue>.

=head1 PUBLIC METHODS

=over 4

=item cPanel::TaskQueue::Scheduler->new( $hashref )

Creates a new TaskQueue::Scheduler object based on the parameters from the
supplied hashref.

=over 4

=item I<state_dir>

This required parameter specifies a directory where the state should be written.
This directory is created if it does not exist.

=item I<name>

This required parameter specifies the name of the scheduler. This name is used
to construct the name of the state file used to store the scheduler information.

=item I<state_timeout>

This optional parameter specifies the timeout to use for flocking the state file.
The value is in seconds and defaults to the cPanel::StateFile default value.

=item I<cache_timeout>

I<Deprecated>. Replaced by the I<state_timeout>.

=item I<token>

If a valid token parameter is supplied, recreate the C<Scheduler> described by
the token. This allows recreating access to a Scheduler that was instantiated
in another process.  It also helps support serializing information about a
C<Scheduler> as part of a defined task.

If a token is supplied, the I<name> and I<state_dir> parameters are ignored
because the token encodes that information.

=back

=item $s->get_name()

Returns the name of the C<Scheduler> object.

=item $s->schedule_task( $command, $hashref )

Schedule the supplied I<command> to be queued as described by the parameters in the supplied I<hashref>.
The I<hashref> has three optional parameters that specify the scheduling time:

=over 4

=item I<at_time>

This parameter specifies a specific time in epoch seconds after which the command will be queued.

=item I<delay_seconds>

This parameter specifies a number of seconds to wait before scheduling the supplied command. If both
I<at_time> and I<delay_seconds> are specified, I<at_time> is used.

=item I<attempts>

Specifies retry count for the task to be rescheduled if the task times out.

=back

=item $s->unschedule_task( $uuid )

Remove the task associated with the supplied I<uuid> from the schedule, if
it has not been processed yet. Returns true on success.

=item $s->get_token()

Returns an opaque string containing the information needed to construct a new
copy of this scheduler. Normally used when requesting a new scheduling at a later
point in time.

=item $s->throw( $msg )

Log the supplied message and C<die>.

=item $s->warn( $msg )

Log the supplied message as a warning.

=item $s->info( $msg )

Log the supplied message as an informational message.

=back

=head2 QUEUE INFORMATION

=over 4

=item $s->peek_next_task()

Get a copy of the next Task to be scheduled or C<undef> if the scheduler is
empty.

Because of the nature of a task scheduler, there is no guarantee that this task
will remain unscheduled after the method call. That is one reason that a copy
is returned.

=item $s->is_task_scheduled( $uuid )

Does the specified I<uuid> reference a task to be scheduled?

Because of the nature of a task scheduler, the particular I<uuid> tested may
be scheduled for processsing immediately after the test. Therefore, a true answer
is not as useful as it might seem. A false answer does tell us that the item is
no longer waiting.

=item $s->when_is_task_scheduled( $uuid )

Returns the time (in epoch seconds) when the Task referenced by I<uuid> is
scheduled to be run or C<undef> if I<uuid> does not reference a valid task.

Because of the nature of a task scheduler, the particular I<uuid> tested may
be scheduled for processsing immediately after the test.

=item $s->how_many_scheduled()

Gives a count at this particular point in time of the number of items currently
in the scheduler. Since an item may be removed and processed any time the
C<process_ready_tasks()> method is called, this count may not be correct immediately
after the method returns.

Most useful for the general case of telling if the queue is really full, or
mostly empty.

=item $s->seconds_until_next_task()

Returns the number of seconds until the next task is ready to be processed, or
C<undef> if there are no tasks to process.

=item $s->snapshot_task_schedule()

Returns an array reference containing a series of hashes continaing the I<time>
a task is scheduled to run and a copy of the I<task> to run at that time. The
first item in the array is guaranteed to be the next task to run. The order of
the rest of the list is not guaranteed.

This lack of guarantee allows the internal code to be implemented as either a sorted
array or a heap without requiring this method to fix up the array.

=back

=head2 SCHEDULING

=over 4

=item $s->process_ready_tasks( $queue )

This method takes all of the Tasks that have reached (or passed) their schedule
time and passes them to the C<queue_task> method of the supplied I<queue> object.
No object is removed from the scheduler unless C<queue_task> runs without an
exception.

In addition, the process of moving a Task from the scheduler to the queue replaces
it's I<uuid>, so don't expect the C<uuid> from the scheduler to have any relation
to the C<uuid> of the same task in the C<TaskQueue>.

Returns the number of tasks processed, C<0> if there were no tasks to process.

=back

=head2 CACHE SUPPORT

These methods should not be used directly, they exist to support the
C<cPanel::StateFile> interface that persists the scheduler information to disk.

=over 4

=item $q->load_from_cache( $fh )

This method loads the scheduler information from the disk state. It is called
by the C<cPanel::StateFile> object owned by this object.

The user of this class should never need to call this method.

=item $q->save_to_cache( $fh )

This method saves the scheduler information to the disk state. It is called by
the C<cPanel::StateFile> object owned by this object.

The user of this class should never need to call this method.

=back

=head1 LOGGER OBJECT

By default, the C<Scheduler> uses C<die> and C<warn> for all messages during
runtime. However, it supports a mechanism that allows you to insert a
logging/reporting system in place by providing an object to do the logging for
us.

To provide a different method of logging/reporting, supply an object to do the
logging as follows when C<use>ing the module.

   use cPanel::TaskQueue::Scheduler ( '-logger' => $logger );

The supplied object should supply (at least) 3 methods: C<throw>, C<warn>, and
C<info>. When needed these methods will be called with the messages to be logged.

The C<throw> method is expected to use C<die> to exit the method. The others
are expected to continue. For example, an appropriate class for C<Log::Log4perl>
might do something like the following:

    package Policy::Log4perl;
    use strict;
    use warnings;
    use Log::Log4perl;

    sub new {
        my ($class) = shift;
        my $self = {
            logger => Log::Log4perl->get_logger( @_ )
        };
        return bless, $class;
    }

    sub throw {
        my $self = shift;
        $self->{logger}->error( @_ );
        die @_;
    }

    sub warn {
        my $self = shift;
        $self->{logger}->warn( @_ );
    }

    sub info {
        my $self = shift;
        $self->{logger}->info( @_ );
    }

This would call the C<Log4perl> code as errors or other messages result in
messages.

This only works once for a given program, so you can't reset the policy in
multiple modules and expect it to work.

In addition to setting a global logger, a new logger object can be supplied
when creating a specific C<Scheduler> object.

=head1 DIAGNOSTICS

The following messages can be reported by this module:

=over 4

=item C<< Invalid token. >>

The I<token> parameter supplied to I<new> is not of the correct form to be a C<cPanel::TaskQueue::Scheduler> token.

=item C<< No caching directory supplied. >>

The required I<state_dir> parameter was missing when constructing the
C<TaskQueue::Scheduler> object. The object was not created.

=item C<< No queue name supplied. >>

The required I<name> parameter was missing when constructing the C<TaskQueue::Scheduler>
object. The object was not created.

=item C<< Not a recognized TaskQueue Scheduler state file. >>

=item C<< Invalid version of TaskQueue Scheduler state file. >>

Either the state file is invalid or it is not a C<cPanel::TaskQueue::Scheduler>
state file.

=item C<< Cannot queue an empty command. >>

The command string supplied to C<schedule_task_*> was either C<undef> or empty.

=item C<< Task with 0 retries not scheduled. >>

The C<Task> supplied to one of the C<schedule_task*> methods has a remaining retry count of 0. The task
has been discarded. This is an informational message only.

=item C<< No Task uuid argument passed to %s. >>

The specified method requires a I<uuid> to specify which task to operate on.
None was supplied.

=item C<< No valid queue supplied. >>

The C<process_ready_tasks> methods requires a C<TaskQueue> as a parameter. (Or, at least, an object with a C<queue_task> method.)

=item C<< Not an even number of arguments to the cPanel::TaskQueue::Scheduler module >>

The parameters passed to the C<import> method should be name/value pairs.

=item C<< Policies already set elsewhere >>

Some other file has already set the policies.

=item C<< Unrecognized policy '%s' >>

The only policy supported by C<Cpane::TaskQueue::Scheduler> is I<-logger>.

=back

=head1 DEPENDENCIES

YAML::Syck

cPanel::TaskQueue, cPanel::TaskQueue::Task, cPanel::StateFile

=head1 INCOMPATIBILITIES

None reported.

=head1 BUGS AND LIMITATIONS

none reported.

=head1 SEE ALSO

cPanel::TaskQueue::Processor, cPanel::TaskQueue::Task, and cPanel::StateFile

=head1 LICENCE AND COPYRIGHT

Copyright (c) 2012, cPanel, Inc. All rights reserved.

This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself. See L<perlartistic>.

=head1 DISCLAIMER OF WARRANTY

BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
NECESSARY SERVICING, REPAIR, OR CORRECTION.

IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
