1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
|
=encoding utf8
=head1 NAME
Minion::Guide - An introduction to Minion
=head1 OVERVIEW
This document contains an introduction to L<Minion> and explains the most important features it has to offer.
=head1 INTRODUCTION
Essentials every L<Minion> developer should know.
=head2 Job queue
Job queues allow you to process time and/or computationally intensive tasks in background processes, outside of the
request/response lifecycle of web applications. Among those tasks you'll commonly find image resizing, spam filtering,
HTTP downloads, building tarballs, warming caches and basically everything else you can imagine that's not super fast.
Mojo::Server::Prefork +--------------+ Minion::Worker
|- Mojo::Server::Daemon [1] enqueue job -> | | -> dequeue job |- Minion::Job [1]
|- Mojo::Server::Daemon [2] | PostgreSQL | |- Minion::Job [2]
|- Mojo::Server::Daemon [3] retrieve result <- | | <- store result |- Minion::Job [3]
+- Mojo::Server::Daemon [4] +--------------+ |- Minion::Job [4]
+- Minion::Job [5]
They are not to be confused with time based job schedulers, such as cron or systemd timers. Both serve very different
purposes, and cron jobs are in fact commonly used to enqueue L<Minion> jobs that need to follow a schedule. For example
to perform regular maintenance tasks.
=head2 Mojolicious
You can use L<Minion> as a standalone job queue or integrate it into L<Mojolicious> applications with the plugin
L<Mojolicious::Plugin::Minion>.
use Mojolicious::Lite -signatures;
plugin Minion => {Pg => 'postgresql://sri:s3cret@localhost/test'};
# Slow task
app->minion->add_task(poke_mojo => sub ($job, @args) {
$job->app->ua->get('mojolicious.org');
$job->app->log->debug('We have poked mojolicious.org for a visitor');
});
# Perform job in a background worker process
get '/' => sub ($c) {
$c->minion->enqueue('poke_mojo');
$c->render(text => 'We will poke mojolicious.org for you soon.');
};
app->start;
Background worker processes are usually started with the command L<Minion::Command::minion::worker>, which becomes
automatically available when an application loads L<Mojolicious::Plugin::Minion>.
$ ./myapp.pl minion worker
The worker process will fork a new process for every job that is being processed. This allows for resources such as
memory to be returned to the operating system once a job is finished. Perl fork is very fast, so don't worry about the
overhead.
Minion::Worker
|- Minion::Job [1]
|- Minion::Job [2]
+- ...
By default up to four jobs will be processed in parallel, but that can be changed with configuration options or on
demand with signals.
$ ./myapp.pl minion worker -j 12
Jobs can be managed right from the command line with L<Minion::Command::minion::job>.
$ ./myapp.pl minion job
You can also add an admin ui to your application by loading the plugin L<Mojolicious::Plugin::Minion::Admin>. Just make
sure to secure access before making your application publicly accessible.
# Make admin ui available under "/minion"
plugin 'Minion::Admin';
=head2 Deployment
To manage background worker processes with systemd, you can use a unit configuration file like this.
[Unit]
Description=My Mojolicious application workers
After=postgresql.service
[Service]
Type=simple
ExecStart=/home/sri/myapp/myapp.pl minion worker -m production
KillMode=process
[Install]
WantedBy=multi-user.target
=head2 Consistency
Every new job starts out as C<inactive>, then progresses to C<active> when it is dequeued by a worker, and finally ends
up as C<finished> or C<failed>, depending on its result. Every C<failed> job can then be retried to progress back to the
C<inactive> state and start all over again.
+----------+
| |
+-----> | finished |
+----------+ +--------+ | | |
| | | | | +----------+
| inactive | -------> | active | ------+
| | | | | +----------+
+----------+ +--------+ | | |
+-----> | failed | -----+
^ | | |
| +----------+ |
| |
+----------------------------------------------------------------+
The system is eventually consistent and will preserve job results for as long as you like, depending on
L<Minion/"remove_after">. But be aware that C<failed> results are preserved indefinitely, and need to be manually
removed by an administrator if they are out of automatic retries.
While individual workers can fail in the middle of processing a job, the system will detect this and ensure that no job
is left in an uncertain state, depending on L<Minion/"missing_after">. Jobs that do not get processed after a certain
amount of time, depending on L<Minion/"stuck_after">, will be considered stuck and fail automatically. So an admin can
take a look and resolve the issue.
=head1 FEATURES
L<Minion> has many great features. This section is still very incomplete, but will be expanded over time.
=head2 Priorities
Every job enqueued with L<"enqueue" in Minion|Minion/"enqueue1"> has a priority. Jobs with a higher priority get
performed first, the default priority is C<0>. Priorities can be positive or negative, but should be in the range
between C<100> and C<-100>.
# Default priority
$minion->enqueue('check_links', ['https://mojolicious.org']);
# High priority
$minion->enqueue('check_links', ['https://mojolicious.org'], {priority => 30});
# Low priority
$minion->enqueue('check_links', ['https://mojolicious.org'], {priority => -30});
You can use L<Minion::Job/"retry"> to raise or lower the priority of a job.
$job->retry({priority => 50});
=head2 Job results
The result of a job has two parts. First there is its state, which can be C<finished> for a successfully processed job,
and C<failed> for the opposite. And second there's a C<result> data structure, that may be C<undef>, a scalar, a hash
reference, or an array reference. You can check both at any time in the life cycle of a job with L<Minion/"job">, all
you need is the job id.
# Check job state
my $state = $minion->job($job_id)->info->{state};
# Get job result
my $result = $minion->job($job_id)->info->{result};
While the C<state> will be assigned automatically by L<Minion>, the C<result> for C<finished> jobs is usually assigned
manually with L<"finish" in Minion::Job|Minion::Job/"finish1">.
$minion->add_task(job_with_result => sub ($job) {
sleep 5;
$job->finish({message => 'This job should have taken about 5 seconds'});
});
For jobs that C<failed> due to an exception, that exception will be assigned as C<result>.
$minion->add_task(job_that_fails => sub ($job) {
sleep 5;
die 'This job should always fail after 5 seconds';
});
But jobs can also fail manually with L<Minion::Job/"fail">.
$minion->add_task(job_that_fails_with_result => sub ($job) {
sleep 5;
$job->fail({errors => ['This job should fail after 5 seconds']});
});
Retrieving job results is of course completely optional, and it is very common to have jobs where the result is
unimportant.
=head2 Named queues
Each job can be enqueued with L<"enqueue" in Minion|Minion/"enqueue1"> into arbitrarily named queues, independent of all
their other properties. This is commonly used to have separate classes of workers, for example to ensure that free
customers of your web service do not negatively affect your service level agreements with paying customers. The default
named queue is C<default>, but aside from that it has no special properties.
# Use "default" queue
$minion->enqueue('check_links', ['https://mojolicious.org']);
# Use custom "important" queue
$minion->enqueue('check_links', ['https://mojolicious.org'], {queue => 'important'});
For every named queue you can start as many workers as you like with the command L<Minion::Command::minion::worker>. And
each worker can process jobs from multiple named queues. So your workers can have overlapping responsibilities.
$ ./myapp.pl minion worker -q default -q important
There is one special named queue called C<minion_foreground> that you should avoid using directly. It is reserved for
debugging jobs with L<Minion/"foreground">.
=head2 Job progress
Progress information and other job metadata can be stored in notes at any time during the life cycle of a job with
L<Minion::Job/"note">. The metadata can be arbitrary data structures constructed with scalars, hash references and array
references.
$minion->add_task(job_with_progress => sub ($job) {
sleep 1;
$job->note(progress => '25%');
sleep 1;
$job->note(progress => '50%');
sleep 1;
$job->note(progress => '75%');
sleep 1;
$job->note(progress => '100%');
});
Notes, similar to job results, can be retrieved with L<Minion/"job">, all you need is the job id.
# Get job metadata
my $progress = $minion->job($job_id)->info->{notes}{progress};
You can also use notes to store arbitrary metadata with new jobs when you create them with
L<"enqueue" in Minion|Minion/"enqueue1">.
# Create job with metadata
$minion->enqueue('job_with_progress', [], {notes => {progress => 0, something_else => [1, 2, 3]}});
The admin ui provided by L<Mojolicious::Plugin::Minion::Admin> allows searching for jobs containing a certain note, so
you can also use them to tag jobs.
=head2 Delayed jobs
The C<delay> option of L<"enqueue" in Minion|Minion/"enqueue1"> can be used to delay the processing of a job by a
certain amount of seconds (from now).
# Job will not be processed for 60 seconds
$minion->enqueue('check_links', ['https://mojolicious.org'], {delay => 20});
You can use L<Minion::Job/"retry"> to change the delay.
$job->retry({delay => 10});
=head2 Expiring jobs
The C<expire> option of L<"enqueue" in Minion|Minion/"enqueue1"> can be used to limit for how many seconds (from now) a
job should be valid before it expires and gets deleted from the queue.
# Job will vanish if it is not dequeued within 60 seconds
$minion->enqueue('check_links', ['https://mojolicious.org'], {expire => 60});
You can use L<Minion::Job/"retry"> to reset the expiration time.
$job->retry({expire => 30});
=head2 Custom workers
In cases where you don't want to use L<Minion> together with L<Mojolicious>, you can just skip the plugins and write
your own worker scripts.
#!/usr/bin/perl
use strict;
use warnings;
use Minion;
# Connect to backend
my $minion = Minion->new(Pg => 'postgresql://postgres@/test');
# Add tasks
$minion->add_task(something_slow => sub ($job, @args) {
sleep 5;
say 'This is a background worker process.';
});
# Start a worker to perform up to 12 jobs concurrently
my $worker = $minion->worker;
$worker->status->{jobs} = 12;
$worker->run;
The method L<Minion::Worker/"run"> contains all features you would expect from a L<Minion> worker and can be easily
configured with L<Minion::Worker/"status">. For even more customization options L<Minion::Worker> also has a very rich
low level API you could for example use to build workers that do not fork at all.
=head2 Task plugins
As your L<Mojolicious> application grows, you can move tasks into application specific plugins.
package MyApp::Task::PokeMojo;
use Mojo::Base 'Mojolicious::Plugin', -signatures;
sub register ($self, $app, $config) {
$app->minion->add_task(poke_mojo => sub ($job, @args) {
$job->app->ua->get('mojolicious.org');
$job->app->log->debug('We have poked mojolicious.org for a visitor');
});
}
1;
Which are loaded like any other plugin from your application.
# Mojolicious
$app->plugin('MyApp::Task::PokeMojo');
# Mojolicious::Lite
plugin 'MyApp::Task::PokeMojo';
=head2 Task classes
For more flexibility, or if you are using L<Minion> as a standalone job queue, you can also move tasks into dedicated
classes. Allowing the use of Perl features such as inheritance and roles. But be aware that support for task classes is
still B<EXPERIMENTAL> and might change without warning!
package MyApp::Task::PokeMojo;
use Mojo::Base 'Minion::Job', -signatures;
sub run ($self, @args) {
$self->app->ua->get('mojolicious.org');
$self->app->log->debug('We have poked mojolicious.org for a visitor');
}
1;
Task classes are registered just like any other task with L<Minion/"add_task"> and you can even register the same class
with multiple names.
$minion->add_task(poke_mojo => 'MyApp::Task::PokeMojo');
=head1 MORE
You can continue with L<Mojolicious::Guides> now or take a look at the L<Mojolicious
wiki|https://github.com/mojolicious/mojo/wiki>, which contains a lot more documentation and examples by many different
authors.
=head1 SUPPORT
If you have any questions the documentation might not yet answer, don't hesitate to ask in the
L<Forum|https://forum.mojolicious.org> or the official IRC channel C<#mojo> on C<irc.libera.chat>
(L<chat now!|https://web.libera.chat/#mojo>).
=cut
|