The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Dancer2::Plugin::JobScheduler - Plugin for Dancer2 web app to send and query jobs in different job schedulers

VERSION

version 0.006

SYNOPSIS

    use Dancer2;
    BEGIN {
        my %plugin_config = (
            default => 'theschwartz',
            schedulers => {
                theschwartz => {
                    client => 'TheSchwartz',
                    parameters => {
                        handle_uniqkey => 'acknowledge',
                        dbh_callback => 'Database::ManagedHandle->instance',
                        databases => {
                            theschwartz_db1 => {
                                prefix => q{schema_name.},
                            },
                        }
                    }
                }
            }
        );
        set log => 'debug';
        set plugins => {
            JobScheduler => \%plugin_config,
        };
    }
    use Dancer2::Plugin::JobScheduler;

    set serializer => 'JSON';

    get q{/submit_job} => sub {
        my %r = submit_job(
            client => 'theschwartz',
            job => {
                task => 'task1',
                args => { name => 'My Name', age => 123 },
                opts => {},
            },
        );
        return to_json(\%r);
    };

    get q{/list_jobs} => sub {
        my %r = list_jobs(
            client => 'theschwartz',
            search_params => {
                task => 'task1',
            },
        );
        return to_json(\%r);
    };

DESCRIPTION

Dancer2::Plugin::JobScheduler is an interface to access different job schedulers in Dancer2 web app.

Dancer2::Plugin::JobScheduler provides an interface to submit jobs and query jobs currently in queue. As a Dancer2 plugin, it creates two new commands in the web app: submit and list_jobs.

These commands abstract away the complexity of interfacing with a job scheduler. User does not need to even know which job scheduler the website is using, unless there are several in use, in which case they can be identified by a short id.

A job scheduler is used to off-load CPU power or time consuming tasks from the web app so that it can answer user's web requests as quickly as possible. One example of tasks like these is sending a confirmation email. The email can be sent after a delay, so the sending is scheduled off to a worker server somewhere else where it will not burden the web app.

There are many job schedulers, and since their operation is separated from Dancer2 web app, they can be implemented in any language, not just Perl, the language of Dancer2.

Perl has several job schedulers, too. Most notable ones are TheSchwartz and Minion. Also Gearman is often mentioned among job schedulers because Gearman's original version was written in Perl though later it was rewritten in C.

Dancer2::Plugin::JobScheduler supports the following job schedulers:

TheSchwartz

Using Dancer2::Plugin::JobScheduler with Dancer2::Plugin::Database

If you are doing database operations in a Dancer2 web app, you are probably using Dancer2::Plugin::Database to get the database handle you need. You can use the same database handle with Dancer2::Plugin::JobScheduler. This can be especially useful if you are doing database transactions. If a transaction fails, you would probably want the scheduled job to be removed as well.

You need to configure the databases just like you would normally but without dbh_callback. You would provide the handle callback at the point of calling submit_job() or list_jobs().

    use Dancer2;
    use HTTP::Status qw( :constants status_message );
    BEGIN {
        set log => 'debug';
        set plugins => {
            JobScheduler => {
                default => 'theschwartz',
                schedulers => {
                    theschwartz => {
                        client => 'TheSchwartz',
                        parameters => {
                            databases => {
                                dancer_app_db => { },
                            },
                            dbh_callback => 'replaced-when-calling',
                        }
                    }
                },
            },
            Database => {
                connections => {
                    dancer_app_db => {
                        driver => SQLite,
                        database => '/tmp/dancer.sqlite'
                    },
                },
            },
        };
    }
    use Dancer2::Plugin::JobScheduler;
    use Dancer2::Plugin::Database;
    set serializer => 'JSON';
    get q{/submit_job} => sub {
        my %r = submit_job(
            client => 'theschwartz',
            job => {
                task => 'task1',
                args => { name => 'Mikko', age => 123 },
                opts => {},
            },
            opts => {
                # database is the keyword and command from
                # Dancer2::Plugin::Database. It takes one argument:
                # the database name, similar to our dbh_callback.
                dbh_callback => \&database,
            },
        );
        status HTTP_OK;
        return \%r;
    };

METHODS

submit

Submit a job with arguments to a job scheduler. This can be as simple as following:

    submit_job( job => { task => 'task_name' });

In the example above, submit_job uses the default scheduler. This is enough when there is only one job scheduler.

Parameter job can also have sub parameters:

args can be used to provide a hash of arguments to the task. These are task specific.
opts can be used to provide a hash of options for the job scheduler. These are job scheduler specific and rarely used. They can be used, for example, to submit the job to a particular queue if there is priority queues in the system.
    submit_job(
        job => {
            task => 'task_name',
            args => { name => 'Average Joe', age => 67 },
            opts => { run_after => time + (60*60) },
        },
    );

In the example above, the task is created with a delay of 60 minutes, i.e. the job scheduler TheSchwartz will not attempt to run the task before one hour is passed.

If you have several different job schedulers you can submit jobs to, then use parameter client to identify the one you want to use. The client names are specified in the configuration. You can also specify a default client.

    submit_job(
        client => 'theschwartz',
        job => {
            task => 'task_name',
        },
    );

submit_job will return a hash which contains at least the following items:

success, boolean. Was the operation successful?
status, string. Contains the status of the submit. In the case of success, this will be "OK".
error, string. Contains an error message if a message is available. Can also be undef.

It can also contain other items depending on the job scheduler. In the case of TheSchwartz, after a successful submit there will be item id which contains the id of the new job in the queue.

The following example showcases a very trivial way on how to integrate submit_job into a route:

    post q{/send_email} => sub {
        my $email = body_parameters->{email};
        # Remember to untaint input:
        ($email) = $email =~ m/ ( [a-zA-Z0-9]{1,} @ [a-zA-Z0-9]{1,} ) /msx;
        submit_job(
            job => {
                task => 'send_email',
                args => { email => $email },
            },
        );
    };

list_jobs

Return a list of all active jobs in the job scheduler.

Parameters:

client, string. The scheduler name. Default specified in the configuration.
search_params, hash. These are job scheduler specific.
    set serializer => q{JSON};
    get q{/list_jobs} => sub {
        my %r = list_jobs(
            client => 'theschwartz',
            search_params => {
                task => 'task1',
            },
        );
        return $r{'jobs'};
    };

CONFIGURATION

Dancer2::Plugin::JobScheduler uses Dancer2's configuration system. You can either write your configuration in the config files or specify it in the module.

The different job schedulers have their own configuration needs. As an example we will cover here only TheSchwartz.

SEE ALSO

There is a Dancer2 plugin for Minion: Dancer2::Plugin::Minion.

AUTHOR

Mikko Koivunalho <mikkoi@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2023 by Mikko Koivunalho.

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