File Coverage

blib/lib/Minion.pm
Criterion Covered Total %
statement 39 140 27.8
branch 0 42 0.0
condition 0 12 0.0
subroutine 13 51 25.4
pod 27 27 100.0
total 79 272 29.0


line stmt bran cond sub pod time code
1             package Minion;
2 2     2   371442 use Mojo::Base 'Mojo::EventEmitter';
  2         4  
  2         12  
3              
4 2     2   3387 use Carp qw(croak);
  2         2  
  2         85  
5 2     2   7 use Config;
  2         9  
  2         53  
6 2     2   781 use Minion::Iterator;
  2         4  
  2         10  
7 2     2   839 use Minion::Job;
  2         5  
  2         14  
8 2     2   981 use Minion::Worker;
  2         6  
  2         16  
9 2     2   916 use Mojo::Date;
  2         2755  
  2         10  
10 2     2   65 use Mojo::IOLoop;
  2         2  
  2         10  
11 2     2   47 use Mojo::Loader qw(load_class);
  2         2  
  2         113  
12 2     2   8 use Mojo::Promise;
  2         2  
  2         24  
13 2     2   756 use Mojo::Server;
  2         3955  
  2         16  
14 2     2   144 use Mojo::Util qw(scope_guard steady_time);
  2         5  
  2         154  
15 2     2   1324 use YAML::XS qw(Dump);
  2         8337  
  2         7301  
16              
17             has app => sub { $_[0]{app_ref} = Mojo::Server->new->build_app('Mojo::HelloWorld') }, weak => 1;
18             has 'backend';
19             has backoff => sub { \&_backoff };
20             has missing_after => 1800;
21             has [qw(remove_after stuck_after)] => 172800;
22             has tasks => sub { {} };
23              
24             our $VERSION = '12.0';
25              
26             sub add_task {
27 0     0 1   my ($self, $name, $task) = @_;
28              
29 0 0         unless (ref $task) {
30 0           my $e = load_class $task;
31 0 0         croak ref $e ? $e : qq{Task "$task" missing} if $e;
    0          
32 0 0         croak qq{Task "$task" is not a Minion::Job subclass} unless $task->isa('Minion::Job');
33             }
34 0           $self->tasks->{$name} = $task;
35              
36 0           return $self;
37             }
38              
39 0     0 1   sub broadcast { shift->backend->broadcast(@_) }
40              
41             sub class_for_task {
42 0     0 1   my ($self, $task) = @_;
43 0           my $class = $self->tasks->{$task};
44 0 0 0       return !$class || ref $class ? 'Minion::Job' : $class;
45             }
46              
47             sub dispatch_schedules {
48 0     0 1   my $self = shift;
49 0           my $dispatched = $self->backend->dispatch_schedules;
50 0           for my $info (@$dispatched) {
51 0           $self->emit(enqueue => $info->{job});
52 0           $self->emit(enqueue_from_schedule => $info->{job}, $info->{name});
53             }
54 0           return $dispatched;
55             }
56              
57             sub enqueue {
58 0     0 1   my $self = shift;
59 0           my $id = $self->backend->enqueue(@_);
60 0           $self->emit(enqueue => $id);
61 0           return $id;
62             }
63              
64             sub foreground {
65 0     0 1   my ($self, $id) = @_;
66              
67 0 0         return undef unless my $job = $self->job($id);
68 0 0         return undef unless $job->retry({attempts => 1, queue => 'minion_foreground'});
69              
70             # Reset event loop
71 0           Mojo::IOLoop->reset;
72 0           local $SIG{CHLD} = local $SIG{INT} = local $SIG{TERM} = local $SIG{QUIT} = 'DEFAULT';
73              
74 0           my $worker = $self->worker->register;
75 0           $job = $worker->dequeue(0 => {id => $id, queues => ['minion_foreground']});
76 0           my $err;
77 0 0         if ($job) { defined($err = $job->execute) ? $job->fail($err) : $job->finish }
  0 0          
78 0           $worker->unregister;
79              
80 0 0         return defined $err ? die $err : !!$job;
81             }
82              
83             sub guard {
84 0     0 1   my ($self, $name, $duration, $options) = @_;
85 0           my $time = steady_time + $duration;
86 0 0         return undef unless $self->lock($name, $duration, $options);
87 0 0   0     return scope_guard sub { $self->unlock($name) if steady_time < $time };
  0            
88             }
89              
90 0     0 1   sub history { shift->backend->history }
91              
92 0     0 1   sub is_locked { !shift->lock(shift, 0) }
93              
94             sub job {
95 0     0 1   my ($self, $id) = @_;
96              
97 0 0         return undef unless my $job = $self->_info($id);
98             return $self->class_for_task($job->{task})
99 0           ->new(args => $job->{args}, id => $job->{id}, minion => $self, retries => $job->{retries}, task => $job->{task});
100             }
101              
102 0     0 1   sub jobs { shift->_iterator(1, @_) }
103              
104 0     0 1   sub list_schedules { shift->backend->list_schedules(@_) }
105              
106 0     0 1   sub lock { shift->backend->lock(@_) }
107              
108             sub new {
109 0     0 1   my $self = shift->SUPER::new;
110              
111 0           my $class = 'Minion::Backend::' . shift;
112 0           my $e = load_class $class;
113 0 0         croak ref $e ? $e : qq{Backend "$class" missing} if $e;
    0          
114              
115 0           return $self->backend($class->new(@_)->minion($self));
116             }
117              
118 0     0 1   sub pause_schedule { shift->backend->pause_schedule(@_) }
119              
120 0     0 1   sub perform_jobs { _perform_jobs(0, @_) }
121 0     0 1   sub perform_jobs_in_foreground { _perform_jobs(1, @_) }
122              
123 0     0 1   sub repair { shift->_delegate('repair') }
124              
125 0     0 1   sub reset { shift->_delegate('reset', @_) }
126              
127             sub result_p {
128 0   0 0 1   my ($self, $id, $options) = (shift, shift, shift // {});
129              
130 0           my $promise = Mojo::Promise->new;
131 0     0     my $cb = sub { $self->_result($promise, $id) };
  0            
132 0   0       my $timer = Mojo::IOLoop->recurring($options->{interval} // 3 => $cb);
133 0     0     $promise->finally(sub { Mojo::IOLoop->remove($timer) })->catch(sub { });
  0            
134 0           $cb->();
135              
136 0           return $promise;
137             }
138              
139 0     0 1   sub resume_schedule { shift->backend->resume_schedule(@_) }
140 0     0 1   sub schedule { shift->backend->schedule(@_) }
141              
142 0     0 1   sub stats { shift->backend->stats }
143 0     0 1   sub unlock { shift->backend->unlock(@_) }
144 0     0 1   sub unschedule { shift->backend->unschedule(@_) }
145              
146             sub worker {
147 0     0 1   my $self = shift;
148              
149             # No fork emulation support
150 0 0         croak 'Minion workers do not support fork emulation' if $Config{d_pseudofork};
151              
152 0           my $worker = Minion::Worker->new(minion => $self);
153 0           $self->emit(worker => $worker);
154 0           return $worker;
155             }
156              
157 0     0 1   sub workers { shift->_iterator(0, @_) }
158              
159 0     0     sub _backoff { (shift()**4) + 15 }
160              
161             # Used by the job and schedule commands and admin plugin
162             sub _datetime {
163 0     0     my $hash = shift;
164             $hash->{$_} and $hash->{$_} = Mojo::Date->new($hash->{$_})->to_datetime
165 0   0       for qw(created delayed expires finished last_run next_run notified retried started time);
166 0           return $hash;
167             }
168              
169             sub _delegate {
170 0     0     my ($self, $method) = (shift, shift);
171 0           $self->backend->$method(@_);
172 0           return $self;
173             }
174              
175 0     0     sub _dump { local $YAML::XS::Boolean = 'JSON::PP'; Dump(@_) }
  0            
176              
177             sub _iterator {
178 0   0 0     my ($self, $jobs, $options) = (shift, shift, shift // {});
179 0           return Minion::Iterator->new(minion => $self, options => $options, jobs => $jobs);
180             }
181              
182 0     0     sub _info { shift->backend->list_jobs(0, 1, {ids => [shift]})->{jobs}[0] }
183              
184             sub _perform_jobs {
185 0     0     my ($foreground, $minion, $options) = @_;
186              
187 0           my $worker = $minion->worker;
188 0           while (my $job = $worker->register->dequeue(0, $options)) {
189 0 0         if (!$foreground) { $job->perform }
  0 0          
190 0           elsif (defined(my $err = $job->execute)) { $job->fail($err) }
191 0           else { $job->finish }
192             }
193 0           $worker->unregister;
194             }
195              
196             sub _result {
197 0     0     my ($self, $promise, $id) = @_;
198 0 0         return $promise->resolve unless my $job = $self->_info($id);
199 0 0         if ($job->{state} eq 'finished') { $promise->resolve($job) }
  0 0          
200 0           elsif ($job->{state} eq 'failed') { $promise->reject($job) }
201             }
202              
203             1;
204              
205             =encoding utf8
206              
207             =head1 NAME
208              
209             Minion - Job queue
210              
211             =head1 SYNOPSIS
212              
213             use Minion;
214              
215             # Connect to backend
216             my $minion = Minion->new(Pg => 'postgresql://postgres@/test');
217              
218             # Add tasks
219             $minion->add_task(something_slow => sub ($job, @args) {
220             sleep 5;
221             say 'This is a background worker process.';
222             });
223              
224             # Enqueue jobs
225             $minion->enqueue(something_slow => ['foo', 'bar']);
226             $minion->enqueue(something_slow => [1, 2, 3] => {priority => 5});
227              
228             # Perform jobs for testing
229             $minion->enqueue(something_slow => ['foo', 'bar']);
230             $minion->perform_jobs;
231              
232             # Start a worker to perform up to 12 jobs concurrently
233             my $worker = $minion->worker;
234             $worker->status->{jobs} = 12;
235             $worker->run;
236              
237             =head1 DESCRIPTION
238              
239             =begin html
240              
241            

242             Screenshot
243            

244              
245             =end html
246              
247             L is a high performance job queue for the Perl programming language, with support for multiple named queues,
248             priorities, high priority fast lane, delayed jobs, cron style recurring jobs, job dependencies, job progress, job
249             results, retries with backoff, rate limiting, unique jobs, expiring jobs, statistics, distributed workers, parallel
250             processing, autoscaling, remote control, L admin ui, resource leak protection and
251             multiple backends (such as L).
252              
253             Job queues allow you to process time and/or computationally intensive tasks in background processes, outside of the
254             request/response lifecycle of web applications. Among those tasks you'll commonly find image resizing, spam filtering,
255             HTTP downloads, building tarballs, warming caches and basically everything else you can imagine that's not super fast.
256              
257             Take a look at our excellent documentation in L!
258              
259             =head1 EXAMPLES
260              
261             This distribution also contains a great example application you can use for inspiration. The L
262             checker|https://github.com/mojolicious/minion/tree/main/examples/linkcheck> will show you how to integrate background
263             jobs into well-structured L applications.
264              
265             =head1 EVENTS
266              
267             L inherits all events from L and can emit the following new ones.
268              
269             =head2 enqueue
270              
271             $minion->on(enqueue => sub ($minion, $id) {
272             ...
273             });
274              
275             Emitted after a job has been enqueued, in the process that enqueued it.
276              
277             $minion->on(enqueue => sub ($minion, $id) {
278             say "Job $id has been enqueued.";
279             });
280              
281             =head2 enqueue_from_schedule
282              
283             $minion->on(enqueue_from_schedule => sub ($minion, $id, $name) {
284             ...
285             });
286              
287             Emitted for every job that L has just enqueued, in the process that called it. The C<$name>
288             argument is the schedule name. The L event is also emitted for each of these jobs.
289              
290             $minion->on(enqueue_from_schedule => sub ($minion, $id, $name) {
291             say qq{Schedule "$name" enqueued job $id.};
292             });
293              
294             =head2 worker
295              
296             $minion->on(worker => sub ($minion, $worker) {
297             ...
298             });
299              
300             Emitted in the worker process after it has been created.
301              
302             $minion->on(worker => sub ($minion, $worker) {
303             say "Worker $$ started.";
304             });
305              
306             =head1 ATTRIBUTES
307              
308             L implements the following attributes.
309              
310             =head2 app
311              
312             my $app = $minion->app;
313             $minion = $minion->app(MyApp->new);
314              
315             Application for job queue, defaults to a L object. Note that this attribute is weakened.
316              
317             =head2 backend
318              
319             my $backend = $minion->backend;
320             $minion = $minion->backend(Minion::Backend::Pg->new);
321              
322             Backend, usually a L object.
323              
324             =head2 backoff
325              
326             my $cb = $minion->backoff;
327             $minion = $minion->backoff(sub {...});
328              
329             A callback used to calculate the delay for automatically retried jobs, defaults to C<(retries ** 4) + 15> (15, 16, 31,
330             96, 271, 640...), which means that roughly C<25> attempts can be made in C<21> days.
331              
332             $minion->backoff(sub ($retries) {
333             return ($retries ** 4) + 15 + int(rand 30);
334             });
335              
336             =head2 missing_after
337              
338             my $after = $minion->missing_after;
339             $minion = $minion->missing_after(172800);
340              
341             Amount of time in seconds after which workers without a heartbeat will be considered missing and removed from the
342             registry by L, defaults to C<1800> (30 minutes).
343              
344             =head2 remove_after
345              
346             my $after = $minion->remove_after;
347             $minion = $minion->remove_after(86400);
348              
349             Amount of time in seconds after which jobs that have reached the state C and have no unresolved dependencies
350             will be removed automatically by L, defaults to C<172800> (2 days). It is not recommended to set this value
351             below 2 days.
352              
353             =head2 stuck_after
354              
355             my $after = $minion->stuck_after;
356             $minion = $minion->stuck_after(86400);
357              
358             Amount of time in seconds after which jobs that have not been processed will be considered stuck by L and
359             transition to the C state, defaults to C<172800> (2 days).
360              
361             =head2 tasks
362              
363             my $tasks = $minion->tasks;
364             $minion = $minion->tasks({foo => sub {...}});
365              
366             Registered tasks.
367              
368             =head1 METHODS
369              
370             L inherits all methods from L and implements the following new ones.
371              
372             =head2 add_task
373              
374             $minion = $minion->add_task(foo => sub {...});
375             $minion = $minion->add_task(foo => 'MyApp::Task::Foo');
376              
377             Register a task, which can be a closure or a custom L subclass. Note that support for custom task classes
378             is B and might change without warning!
379              
380             # Job with result
381             $minion->add_task(add => sub ($job, $first, $second) {
382             $job->finish($first + $second);
383             });
384             my $id = $minion->enqueue(add => [1, 1]);
385             my $result = $minion->job($id)->info->{result};
386              
387             =head2 broadcast
388              
389             my $bool = $minion->broadcast('some_command');
390             my $bool = $minion->broadcast('some_command', [@args]);
391             my $bool = $minion->broadcast('some_command', [@args], [$id1, $id2, $id3]);
392              
393             Broadcast remote control command to one or more workers.
394              
395             # Broadcast "stop" command to all workers to kill job 10025
396             $minion->broadcast('stop', [10025]);
397              
398             # Broadcast "kill" command to all workers to interrupt job 10026
399             $minion->broadcast('kill', ['INT', 10026]);
400              
401             # Broadcast "jobs" command to pause worker 23
402             $minion->broadcast('jobs', [0], [23]);
403              
404             # Broadcast "spare" command to disable the feature on all workers
405             $minion->broadcast('spare', [0]);
406              
407             =head2 class_for_task
408              
409             my $class = $minion->class_for_task('foo');
410              
411             Return job class for task. Note that this method is B and might change without warning!
412              
413             =head2 dispatch_schedules
414              
415             my $dispatched = $minion->dispatch_schedules;
416              
417             Enqueue jobs for all schedules whose firing time has been reached, advance their firing times to the next match, and
418             return information about each dispatch as an array reference. Coordinates across multiple workers via a Postgres
419             advisory lock so a single tick never enqueues a schedule twice. Workers tick this automatically, see L.
420              
421             # Manually fire any due schedules
422             for my $info (@{$minion->dispatch_schedules}) {
423             say qq{Schedule "$info->{name}" enqueued job $info->{job}.};
424             }
425              
426             =head2 enqueue
427              
428             my $id = $minion->enqueue('foo');
429             my $id = $minion->enqueue(foo => [@args]);
430             my $id = $minion->enqueue(foo => [@args] => {priority => 1});
431              
432             Enqueue a new job with C state. Arguments get serialized by the L (often with L), so
433             you shouldn't send objects and be careful with binary data, nested data structures with hash and array references are
434             fine though.
435              
436             These options are currently available:
437              
438             =over 2
439              
440             =item attempts
441              
442             attempts => 25
443              
444             Number of times performing this job will be attempted, with a delay based on L after the first attempt,
445             defaults to C<1>.
446              
447             =item delay
448              
449             delay => 10
450              
451             Delay job for this many seconds (from now), defaults to C<0>.
452              
453             =item expire
454              
455             expire => 300
456              
457             Job is valid for this many seconds (from now) before it expires.
458              
459             =item lax
460              
461             lax => 1
462              
463             Existing jobs this job depends on may also have transitioned to the C state to allow for it to be processed,
464             defaults to C.
465              
466             =item notes
467              
468             notes => {foo => 'bar', baz => [1, 2, 3]}
469              
470             Hash reference with arbitrary metadata for this job that gets serialized by the L (often with
471             L), so you shouldn't send objects and be careful with binary data, nested data structures with hash and
472             array references are fine though.
473              
474             =item parents
475              
476             parents => [$id1, $id2, $id3]
477              
478             One or more existing jobs this job depends on, and that need to have transitioned to the state C before it
479             can be processed.
480              
481             =item priority
482              
483             priority => 5
484              
485             Job priority, defaults to C<0>. Jobs with a higher priority get performed first. Priorities can be positive or negative,
486             but should be in the range between C<100> and C<-100>.
487              
488             =item queue
489              
490             queue => 'important'
491              
492             Queue to put job in, defaults to C.
493              
494             =back
495              
496             =head2 foreground
497              
498             my $bool = $minion->foreground($id);
499              
500             Retry job in C queue, then perform it right away with a temporary worker in this process, very
501             useful for debugging.
502              
503             =head2 guard
504              
505             my $guard = $minion->guard('foo', 3600);
506             my $guard = $minion->guard('foo', 3600, {limit => 20});
507              
508             Same as L, but returns a scope guard object that automatically releases the lock as soon as the object is
509             destroyed, or C if aquiring the lock failed.
510              
511             # Only one job should run at a time (unique job)
512             $minion->add_task(do_unique_stuff => sub ($job, @args) {
513             return $job->finish('Previous job is still active')
514             unless my $guard = $minion->guard('fragile_backend_service', 7200);
515             ...
516             });
517              
518             # Only five jobs should run at a time and we try again later if necessary
519             $minion->add_task(do_concurrent_stuff => sub ($job, @args) {
520             return $job->retry({delay => 30})
521             unless my $guard = $minion->guard('some_web_service', 60, {limit => 5});
522             ...
523             });
524              
525             =head2 history
526              
527             my $history = $minion->history;
528              
529             Get history information for job queue.
530              
531             These fields are currently available:
532              
533             =over 2
534              
535             =item daily
536              
537             daily => [{epoch => 12345, finished_jobs => 95, failed_jobs => 2}, ...]
538              
539             Hourly counts for processed jobs from the past day.
540              
541             =back
542              
543             =head2 is_locked
544              
545             my $bool = $minion->is_locked('foo');
546              
547             Check if a lock with that name is currently active.
548              
549             =head2 job
550              
551             my $job = $minion->job($id);
552              
553             Get L object without making any changes to the actual job or return C if job does not exist.
554              
555             # Check job state
556             my $state = $minion->job($id)->info->{state};
557              
558             # Get job metadata
559             my $progress = $minion->job($id)->info->{notes}{progress};
560              
561             # Get job result
562             my $result = $minion->job($id)->info->{result};
563              
564             =head2 jobs
565              
566             my $jobs = $minion->jobs;
567             my $jobs = $minion->jobs({states => ['inactive']});
568              
569             Return L object to safely iterate through job information.
570              
571             # Iterate through jobs for two tasks
572             my $jobs = $minion->jobs({tasks => ['foo', 'bar']});
573             while (my $info = $jobs->next) {
574             say "$info->{id}: $info->{state}";
575             }
576              
577             # Remove all failed jobs from a named queue
578             my $jobs = $minion->jobs({states => ['failed'], queues => ['unimportant']});
579             while (my $info = $jobs->next) {
580             $minion->job($info->{id})->remove;
581             }
582              
583             # Count failed jobs for a task
584             say $minion->jobs({states => ['failed'], tasks => ['foo']})->total;
585              
586             These options are currently available:
587              
588             =over 2
589              
590             =item ids
591              
592             ids => ['23', '24']
593              
594             List only jobs with these ids.
595              
596             =item notes
597              
598             notes => ['foo', 'bar']
599              
600             List only jobs with one of these notes.
601              
602             =item queues
603              
604             queues => ['important', 'unimportant']
605              
606             List only jobs in these queues.
607              
608             =item states
609              
610             states => ['inactive', 'active']
611              
612             List only jobs in these states.
613              
614             =item tasks
615              
616             tasks => ['foo', 'bar']
617              
618             List only jobs for these tasks.
619              
620             =back
621              
622             These fields are currently available:
623              
624             =over 2
625              
626             =item args
627              
628             args => ['foo', 'bar']
629              
630             Job arguments.
631              
632             =item attempts
633              
634             attempts => 25
635              
636             Number of times performing this job will be attempted.
637              
638             =item children
639              
640             children => ['10026', '10027', '10028']
641              
642             Jobs depending on this job.
643              
644             =item created
645              
646             created => 784111777
647              
648             Epoch time job was created.
649              
650             =item delayed
651              
652             delayed => 784111777
653              
654             Epoch time job was delayed to.
655              
656             =item expires
657              
658             expires => 784111777
659              
660             Epoch time job is valid until before it expires.
661              
662             =item finished
663              
664             finished => 784111777
665              
666             Epoch time job was finished.
667              
668             =item id
669              
670             id => 10025
671              
672             Job id.
673              
674             =item lax
675              
676             lax => 0
677              
678             Existing jobs this job depends on may also have failed to allow for it to be processed.
679              
680             =item notes
681              
682             notes => {foo => 'bar', baz => [1, 2, 3]}
683              
684             Hash reference with arbitrary metadata for this job.
685              
686             =item parents
687              
688             parents => ['10023', '10024', '10025']
689              
690             Jobs this job depends on.
691              
692             =item priority
693              
694             priority => 3
695              
696             Job priority.
697              
698             =item queue
699              
700             queue => 'important'
701              
702             Queue name.
703              
704             =item result
705              
706             result => 'All went well!'
707              
708             Job result.
709              
710             =item retried
711              
712             retried => 784111777
713              
714             Epoch time job has been retried.
715              
716             =item retries
717              
718             retries => 3
719              
720             Number of times job has been retried.
721              
722             =item started
723              
724             started => 784111777
725              
726             Epoch time job was started.
727              
728             =item state
729              
730             state => 'inactive'
731              
732             Current job state, usually C, C, C or C.
733              
734             =item task
735              
736             task => 'foo'
737              
738             Task name.
739              
740             =item time
741              
742             time => 78411177
743              
744             Server time.
745              
746             =item worker
747              
748             worker => '154'
749              
750             Id of worker that is processing the job.
751              
752             =back
753              
754             =head2 list_schedules
755              
756             my $results = $minion->list_schedules(0, 10);
757             my $results = $minion->list_schedules(0, 10, {names => ['daily']});
758              
759             Return information about schedules in batches.
760              
761             # Get the total number of schedules
762             my $num = $minion->list_schedules(0, 100)->{total};
763              
764             # Inspect the next firing time of a schedule by name
765             my $next = $minion->list_schedules(0, 1, {names => ['daily']})->{schedules}[0]{next_run};
766              
767             These options are currently available:
768              
769             =over 2
770              
771             =item before
772              
773             before => 23
774              
775             List only schedules before this id.
776              
777             =item ids
778              
779             ids => ['23', '24']
780              
781             List only schedules with these ids.
782              
783             =item names
784              
785             names => ['foo', 'bar']
786              
787             List only schedules with these names.
788              
789             =back
790              
791             These fields are currently available:
792              
793             =over 2
794              
795             =item args
796              
797             args => ['foo', 'bar']
798              
799             Job arguments.
800              
801             =item attempts
802              
803             attempts => 25
804              
805             Number of attempts each enqueued job will get.
806              
807             =item created
808              
809             created => 784111777
810              
811             Epoch time the schedule was created.
812              
813             =item cron
814              
815             cron => '0 9 * * 1-5'
816              
817             Cron expression.
818              
819             =item expire
820              
821             expire => 300
822              
823             Expiration in seconds for each enqueued job.
824              
825             =item id
826              
827             id => 23
828              
829             Schedule id.
830              
831             =item last_job
832              
833             last_job => '10025'
834              
835             Id of the most recently enqueued job, or C if the schedule has not fired yet.
836              
837             =item last_run
838              
839             last_run => 784111777
840              
841             Epoch time the schedule last fired, or C if it has not fired yet.
842              
843             =item lax
844              
845             lax => 0
846              
847             Lax dependency setting for each enqueued job.
848              
849             =item name
850              
851             name => 'daily'
852              
853             Schedule name.
854              
855             =item next_run
856              
857             next_run => 784111777
858              
859             Epoch time the schedule will fire next.
860              
861             =item notes
862              
863             notes => {foo => 'bar'}
864              
865             Hash reference with arbitrary metadata applied to each enqueued job.
866              
867             =item paused
868              
869             paused => 0
870              
871             True if the schedule is paused and will not fire.
872              
873             =item priority
874              
875             priority => 0
876              
877             Priority of each enqueued job.
878              
879             =item queue
880              
881             queue => 'default'
882              
883             Queue each enqueued job is placed in.
884              
885             =item task
886              
887             task => 'foo'
888              
889             Task name.
890              
891             =back
892              
893             =head2 lock
894              
895             my $bool = $minion->lock('foo', 3600);
896             my $bool = $minion->lock('foo', 3600, {limit => 20});
897              
898             Try to acquire a named lock that will expire automatically after the given amount of time in seconds. You can release
899             the lock manually with L to limit concurrency, or let it expire for rate limiting. For convenience you can
900             also use L to release the lock automatically, even if the job failed.
901              
902             # Only one job should run at a time (unique job)
903             $minion->add_task(do_unique_stuff => sub ($job, @args) {
904             return $job->finish('Previous job is still active')
905             unless $minion->lock('fragile_backend_service', 7200);
906             ...
907             $minion->unlock('fragile_backend_service');
908             });
909              
910             # Only five jobs should run at a time and we wait for our turn
911             $minion->add_task(do_concurrent_stuff => sub ($job, @args) {
912             sleep 1 until $minion->lock('some_web_service', 60, {limit => 5});
913             ...
914             $minion->unlock('some_web_service');
915             });
916              
917             # Only a hundred jobs should run per hour and we try again later if necessary
918             $minion->add_task(do_rate_limited_stuff => sub ($job, @args) {
919             return $job->retry({delay => 3600})
920             unless $minion->lock('another_web_service', 3600, {limit => 100});
921             ...
922             });
923              
924             An expiration time of C<0> can be used to check if a named lock could have been acquired without creating one.
925              
926             # Check if the lock "foo" could have been acquired
927             say 'Lock could have been acquired' unless $minion->lock('foo', 0);
928              
929             Or to simply check if a named lock already exists you can also use L.
930              
931             These options are currently available:
932              
933             =over 2
934              
935             =item limit
936              
937             limit => 20
938              
939             Number of shared locks with the same name that can be active at the same time, defaults to C<1>.
940              
941             =back
942              
943             =head2 new
944              
945             my $minion = Minion->new(Pg => 'postgresql://postgres@/test');
946             my $minion = Minion->new(Pg => Mojo::Pg->new);
947              
948             Construct a new L object.
949              
950             =head2 pause_schedule
951              
952             my $bool = $minion->pause_schedule('daily');
953              
954             Pause a schedule by name so it stops firing until resumed. Returns true on success, false if the schedule does not
955             exist.
956              
957             =head2 perform_jobs
958              
959             $minion->perform_jobs;
960             $minion->perform_jobs({queues => ['important']});
961              
962             Perform all jobs with a temporary worker, very useful for testing.
963              
964             # Longer version
965             my $worker = $minion->worker;
966             while (my $job = $worker->register->dequeue(0)) { $job->perform }
967             $worker->unregister;
968              
969             These options are currently available:
970              
971             =over 2
972              
973             =item id
974              
975             id => '10023'
976              
977             Dequeue a specific job.
978              
979             =item min_priority
980              
981             min_priority => 3
982              
983             Do not dequeue jobs with a lower priority.
984              
985             =item queues
986              
987             queues => ['important']
988              
989             One or more queues to dequeue jobs from, defaults to C.
990              
991             =back
992              
993             =head2 perform_jobs_in_foreground
994              
995             $minion->perform_jobs_in_foreground;
996             $minion->perform_jobs_in_foreground({queues => ['important']});
997              
998             Same as L, but all jobs are performed in the current process, without spawning new processes.
999              
1000             =head2 repair
1001              
1002             $minion = $minion->repair;
1003              
1004             Repair worker registry and job queue if necessary.
1005              
1006             =head2 reset
1007              
1008             $minion = $minion->reset({all => 1});
1009              
1010             Reset job queue.
1011              
1012             These options are currently available:
1013              
1014             =over 2
1015              
1016             =item all
1017              
1018             all => 1
1019              
1020             Reset everything.
1021              
1022             =item locks
1023              
1024             locks => 1
1025              
1026             Reset only locks.
1027              
1028             =back
1029              
1030             =head2 result_p
1031              
1032             my $promise = $minion->result_p($id);
1033             my $promise = $minion->result_p($id, {interval => 5});
1034              
1035             Return a L object for the result of a job. The state C will result in the promise being
1036             C, and the state C in the promise being C. This operation can be cancelled by resolving
1037             the promise manually at any time.
1038              
1039             # Enqueue job and receive the result at some point in the future
1040             my $id = $minion->enqueue('foo');
1041             $minion->result_p($id)->then(sub ($info) {
1042             my $result = ref $info ? $info->{result} : 'Job already removed';
1043             say "Finished: $result";
1044             })->catch(sub ($info) {
1045             say "Failed: $info->{result}";
1046             })->wait;
1047              
1048             These options are currently available:
1049              
1050             =over 2
1051              
1052             =item interval
1053              
1054             interval => 5
1055              
1056             Polling interval in seconds for checking if the state of the job has changed, defaults to C<3>.
1057              
1058             =back
1059              
1060             =head2 resume_schedule
1061              
1062             my $bool = $minion->resume_schedule('daily');
1063              
1064             Resume a previously paused schedule. Returns true on success, false if the schedule does not exist.
1065              
1066             =head2 schedule
1067              
1068             my $id = $minion->schedule('daily', '0 4 * * *', 'cleanup');
1069             my $id = $minion->schedule('daily', '0 4 * * *', 'cleanup', [@args]);
1070             my $id = $minion->schedule(
1071             'daily', '0 4 * * *', 'cleanup', [@args], {priority => 5});
1072              
1073             Create or update a schedule by unique name. The cron expression is parsed up front, so invalid expressions raise an
1074             exception immediately. The expression uses the standard five field form (minute, hour, day-of-month, month,
1075             day-of-week), with C<*>, C<*/N>, C, C and comma separated lists, the names C-C and
1076             C-C, and the nicknames C<@yearly>, C<@monthly>, C<@weekly>, C<@daily>, C<@midnight> and C<@hourly>. All
1077             times are interpreted in B, not local time. Updating a schedule with the same cron expression preserves its current
1078             firing time; changing the expression recomputes it.
1079              
1080             # A daily job
1081             $minion->schedule(daily => '0 4 * * *' => 'cleanup');
1082              
1083             # Every 5 minutes with arguments and options
1084             $minion->schedule(
1085             every_5min => '*/5 * * * *' => 'foo' => [1, 2, 3] => {priority => 5, queue => 'important'});
1086              
1087             # Weekdays at 9 with retries
1088             $minion->schedule(weekday_report => '0 9 * * 1-5' => 'report' => [] => {attempts => 3});
1089              
1090             These options are currently available:
1091              
1092             =over 2
1093              
1094             =item attempts
1095              
1096             attempts => 25
1097              
1098             Number of times performing each enqueued job will be attempted, defaults to C<1>.
1099              
1100             =item expire
1101              
1102             expire => 300
1103              
1104             Each enqueued job is valid for this many seconds before it expires.
1105              
1106             =item lax
1107              
1108             lax => 1
1109              
1110             Existing jobs each enqueued job depends on may also have transitioned to the C state, defaults to C.
1111              
1112             =item notes
1113              
1114             notes => {foo => 'bar'}
1115              
1116             Hash reference with arbitrary metadata applied to each enqueued job.
1117              
1118             =item priority
1119              
1120             priority => 5
1121              
1122             Priority of each enqueued job, defaults to C<0>.
1123              
1124             =item queue
1125              
1126             queue => 'important'
1127              
1128             Queue to put each enqueued job in, defaults to C.
1129              
1130             =back
1131              
1132             =head2 stats
1133              
1134             my $stats = $minion->stats;
1135              
1136             Get statistics for the job queue.
1137              
1138             # Check idle workers
1139             my $idle = $minion->stats->{inactive_workers};
1140              
1141             These fields are currently available:
1142              
1143             =over 2
1144              
1145             =item active_jobs
1146              
1147             active_jobs => 100
1148              
1149             Number of jobs in C state.
1150              
1151             =item active_locks
1152              
1153             active_locks => 100
1154              
1155             Number of active named locks.
1156              
1157             =item active_workers
1158              
1159             active_workers => 100
1160              
1161             Number of workers that are currently processing a job.
1162              
1163             =item delayed_jobs
1164              
1165             delayed_jobs => 100
1166              
1167             Number of jobs in C state that are scheduled to run at specific time in the future or have unresolved
1168             dependencies.
1169              
1170             =item enqueued_jobs
1171              
1172             enqueued_jobs => 100000
1173              
1174             Rough estimate of how many jobs have ever been enqueued.
1175              
1176             =item failed_jobs
1177              
1178             failed_jobs => 100
1179              
1180             Number of jobs in C state.
1181              
1182             =item finished_jobs
1183              
1184             finished_jobs => 100
1185              
1186             Number of jobs in C state.
1187              
1188             =item inactive_jobs
1189              
1190             inactive_jobs => 100
1191              
1192             Number of jobs in C state.
1193              
1194             =item inactive_schedules
1195              
1196             inactive_schedules => 100
1197              
1198             Number of schedules that are currently paused.
1199              
1200             =item inactive_workers
1201              
1202             inactive_workers => 100
1203              
1204             Number of workers that are currently not processing a job.
1205              
1206             =item schedules
1207              
1208             schedules => 100
1209              
1210             Number of schedules that are currently active.
1211              
1212             =item uptime
1213              
1214             uptime => 1000
1215              
1216             Uptime in seconds.
1217              
1218             =item workers
1219              
1220             workers => 200;
1221              
1222             Number of registered workers.
1223              
1224             =back
1225              
1226             =head2 unlock
1227              
1228             my $bool = $minion->unlock('foo');
1229              
1230             Release a named lock that has been previously acquired with L.
1231              
1232             =head2 unschedule
1233              
1234             my $bool = $minion->unschedule('daily');
1235              
1236             Remove a schedule by name. Returns true on success, false if the schedule does not exist.
1237              
1238             =head2 worker
1239              
1240             my $worker = $minion->worker;
1241              
1242             Build L object. Note that this method should only be used to implement custom workers.
1243              
1244             # Use the standard worker with all its features
1245             my $worker = $minion->worker;
1246             $worker->status->{jobs} = 12;
1247             $worker->status->{queues} = ['important'];
1248             $worker->run;
1249              
1250             # Perform one job manually in a separate process
1251             my $worker = $minion->repair->worker->register;
1252             my $job = $worker->dequeue(5);
1253             $job->perform;
1254             $worker->unregister;
1255              
1256             # Perform one job manually in this process
1257             my $worker = $minion->repair->worker->register;
1258             my $job = $worker->dequeue(5);
1259             if (my $err = $job->execute) { $job->fail($err) }
1260             else { $job->finish }
1261             $worker->unregister;
1262              
1263             # Build a custom worker performing multiple jobs at the same time
1264             my %jobs;
1265             my $worker = $minion->repair->worker->register;
1266             do {
1267             for my $id (keys %jobs) {
1268             delete $jobs{$id} if $jobs{$id}->is_finished;
1269             }
1270             if (keys %jobs >= 4) { sleep 5 }
1271             else {
1272             my $job = $worker->dequeue(5);
1273             $jobs{$job->id} = $job->start if $job;
1274             }
1275             } while keys %jobs;
1276             $worker->unregister;
1277              
1278             =head2 workers
1279              
1280             my $workers = $minion->workers;
1281             my $workers = $minion->workers({ids => [2, 3]});
1282              
1283             Return L object to safely iterate through worker information.
1284              
1285             # Iterate through workers
1286             my $workers = $minion->workers;
1287             while (my $info = $workers->next) {
1288             say "$info->{id}: $info->{host}";
1289             }
1290              
1291             These options are currently available:
1292              
1293             =over 2
1294              
1295             =item ids
1296              
1297             ids => ['23', '24']
1298              
1299             List only workers with these ids.
1300              
1301             =back
1302              
1303             These fields are currently available:
1304              
1305             =over 2
1306              
1307             =item id
1308              
1309             id => 22
1310              
1311             Worker id.
1312              
1313             =item host
1314              
1315             host => 'localhost'
1316              
1317             Worker host.
1318              
1319             =item jobs
1320              
1321             jobs => ['10023', '10024', '10025', '10029']
1322              
1323             Ids of jobs the worker is currently processing.
1324              
1325             =item notified
1326              
1327             notified => 784111777
1328              
1329             Epoch time worker sent the last heartbeat.
1330              
1331             =item pid
1332              
1333             pid => 12345
1334              
1335             Process id of worker.
1336              
1337             =item started
1338              
1339             started => 784111777
1340              
1341             Epoch time worker was started.
1342              
1343             =item status
1344              
1345             status => {queues => ['default', 'important']}
1346              
1347             Hash reference with whatever status information the worker would like to share.
1348              
1349             =back
1350              
1351             =head1 API
1352              
1353             This is the class hierarchy of the L distribution.
1354              
1355             =over 2
1356              
1357             =item * L
1358              
1359             =item * L
1360              
1361             =over 2
1362              
1363             =item * L
1364              
1365             =back
1366              
1367             =item * L
1368              
1369             =item * L
1370              
1371             =item * L
1372              
1373             =item * L
1374              
1375             =item * L
1376              
1377             =item * L
1378              
1379             =item * L
1380              
1381             =item * L
1382              
1383             =item * L
1384              
1385             =back
1386              
1387             =head1 BUNDLED FILES
1388              
1389             The L distribution includes a few files with different licenses that have been bundled for internal use.
1390              
1391             =head2 Minion Artwork
1392              
1393             Copyright (C) 2017, Sebastian Riedel.
1394              
1395             Licensed under the CC-SA License, Version 4.0 L.
1396              
1397             =head2 Bootstrap
1398              
1399             Copyright (C) 2011-2021 The Bootstrap Authors.
1400              
1401             Licensed under the MIT License, L.
1402              
1403             =head2 jQuery
1404              
1405             Copyright (C) jQuery Foundation.
1406              
1407             Licensed under the MIT License, L.
1408              
1409             =head2 D3.js
1410              
1411             Copyright (C) 2010-2016, Michael Bostock.
1412              
1413             Licensed under the 3-Clause BSD License, L.
1414              
1415             =head2 epoch.js
1416              
1417             Copyright (C) 2014 Fastly, Inc.
1418              
1419             Licensed under the MIT License, L.
1420              
1421             =head2 Font Awesome
1422              
1423             Copyright (C) Dave Gandy.
1424              
1425             Licensed under the MIT License, L, and the SIL OFL 1.1,
1426             L.
1427              
1428             =head2 moment.js
1429              
1430             Copyright (C) JS Foundation and other contributors.
1431              
1432             Licensed under the MIT License, L.
1433              
1434             =head1 AUTHORS
1435              
1436             =head2 Project Founder
1437              
1438             Sebastian Riedel, C.
1439              
1440             =head2 Contributors
1441              
1442             In alphabetical order:
1443              
1444             =over 2
1445              
1446             Andrey Khozov
1447              
1448             Andrii Nikitin
1449              
1450             Brian Medley
1451              
1452             Franz Skale
1453              
1454             Henrik Andersen
1455              
1456             Hubert "depesz" Lubaczewski
1457              
1458             Joel Berger
1459              
1460             Paul Williams
1461              
1462             Russell Shingleton
1463              
1464             Stefan Adams
1465              
1466             Stuart Skelton
1467              
1468             =back
1469              
1470             =head1 COPYRIGHT AND LICENSE
1471              
1472             Copyright (C) 2014-2024, Sebastian Riedel and others.
1473              
1474             This program is free software, you can redistribute it and/or modify it under the terms of the Artistic License version
1475             2.0.
1476              
1477             =head1 SEE ALSO
1478              
1479             L, L, L, L,
1480             L.
1481              
1482             =cut