The Minion job queue is an incredibly useful tool, but sometimes I need to use it for non-web projects. So, how can I use Minion without needing a Mojolicious web application?

If I don't have a large enough set of jobs to need the task organization provided by Beam::Minion, and although Minion can be used as a fully stand-alone library, the easiest solution is just to build a Mojolicious app. Lucky for me, a Mojolicious app can be just 2 lines:

use Mojolicious::Lite;
app->start;

Now, if I never run a web daemon, this will never be a website. So, can this be called a web application if nobody ever uses a web browser to access it? 🤔

Add Minion

To use Minion, add the Minion plugin to the app. For this example, I'll use the SQLite Minion backend so that I don't need a separate database process running, but Minion can work across multiple machines if you have a database that accepts remote connections.

plugin Minion => {
    SQLite => 'sqlite:' . app->home->child('minion.db'),
};

With the Minion plugin loaded, my application gains some new features:

Create a Task

I'll create a task called check_url to check how long it takes to download a URL. The Time::HiRes core module will give me high resolution times.

#!/usr/bin/env perl

use v5.28;
use Mojolicious::Lite;
use experimental qw( signatures );
use Time::HiRes qw( time );

plugin Minion => {
    SQLite => 'sqlite:' . app->home->child('minion.db'),
};

app->minion->add_task(
    check_url => sub( $job, $url ) {
        my $start = time;
        my $tx = $job->app->ua->head( $url );
        my $elapsed = time - $start;
        $job->app->log->info(
            sprintf 'Fetching %s took %.3f seconds', $url, $elapsed
        );
        # If there's an error loading the web page, fail the job
        if ( $tx->error ) {
            $job->app->log->error(
                sprintf 'Error loading URL (%s): %s (%s)',
                    $url, @{ $tx->error }{qw( code message )},
            );
            return $job->fail(
                sprintf '%s: %s', @{ $tx->error }{qw( code message )}
            );
        }
        $job->finish( $elapsed );
    },
);

app->start;

Enqueuing Jobs

Now that I have a task, I can enqueue some jobs. I can add jobs using the minion job command:

$ perl myapp.pl minion job -e check_url -a '["http://mojolicious.org"]'

Or from inside of another Perl script by loading Minion and using the enqueue method:

#!/usr/bin/env perl
use Minion;
my $minion = Minion->new(
    SQLite => 'sqlite:minion.db', # The same database as the worker
);
$minion->enqueue(
    check_url => ['http://mojolicious.org'],
);

Running a Worker

I've enqueued jobs, but nothing's happening, and nothing will happen until I run a worker using the minion worker command:

$ perl myapp.pl minion worker

Once the worker starts up, it will immediately begin processing the jobs I told it to run.

And that's it! I'm using Minion without a Mojolicious web application. View the source of the Minion app and the enqueue.pl script.

Original artwork by Doug Bell, released under CC-BY-SA 4.0. It includes a screenshot of the Mojolicious.org website (fair use), the Mojolicious logo (CC-BY-SA 4.0), and the Minion logo (CC-BY-SA 4.0)

Tagged in : advent, minion

author image
Doug Bell

Doug (preaction) is a long time Perl user. He is the current maintainer of CPAN Testers and the author of many CPAN modules including the Statocles blog engine that powers this site.