1Minion(3)             User Contributed Perl Documentation            Minion(3)
2
3
4

NAME

6       Minion - Job queue
7

SYNOPSIS

9         use Minion;
10
11         # Connect to backend
12         my $minion = Minion->new(Pg => 'postgresql://postgres@/test');
13
14         # Add tasks
15         $minion->add_task(something_slow => sub ($job, @args) {
16           sleep 5;
17           say 'This is a background worker process.';
18         });
19
20         # Enqueue jobs
21         $minion->enqueue(something_slow => ['foo', 'bar']);
22         $minion->enqueue(something_slow => [1, 2, 3] => {priority => 5});
23
24         # Perform jobs for testing
25         $minion->enqueue(something_slow => ['foo', 'bar']);
26         $minion->perform_jobs;
27
28         # Start a worker to perform up to 12 jobs concurrently
29         my $worker = $minion->worker;
30         $worker->status->{jobs} = 12;
31         $worker->run;
32

DESCRIPTION

34       Minion is a high performance job queue for the Perl programming
35       language, with support for multiple named queues, priorities, delayed
36       jobs, job dependencies, job progress, job results, retries with
37       backoff, rate limiting, unique jobs, expiring jobs, statistics,
38       distributed workers, parallel processing, autoscaling, remote control,
39       Mojolicious <https://mojolicious.org> admin ui, resource leak
40       protection and multiple backends (such as PostgreSQL
41       <https://www.postgresql.org>).
42
43       Job queues allow you to process time and/or computationally intensive
44       tasks in background processes, outside of the request/response
45       lifecycle of web applications. Among those tasks you'll commonly find
46       image resizing, spam filtering, HTTP downloads, building tarballs,
47       warming caches and basically everything else you can imagine that's not
48       super fast.
49

BASICS

51       You can use Minion as a standalone job queue or integrate it into
52       Mojolicious applications with the plugin Mojolicious::Plugin::Minion.
53
54         use Mojolicious::Lite -signatures;
55
56         plugin Minion => {Pg => 'postgresql://sri:s3cret@localhost/test'};
57
58         # Slow task
59         app->minion->add_task(poke_mojo => sub ($job, @args) {
60           $job->app->ua->get('mojolicious.org');
61           $job->app->log->debug('We have poked mojolicious.org for a visitor');
62         });
63
64         # Perform job in a background worker process
65         get '/' => sub ($c) {
66           $c->minion->enqueue('poke_mojo');
67           $c->render(text => 'We will poke mojolicious.org for you soon.');
68         };
69
70         app->start;
71
72       Background worker processes are usually started with the command
73       Minion::Command::minion::worker, which becomes automatically available
74       when an application loads Mojolicious::Plugin::Minion.
75
76         $ ./myapp.pl minion worker
77
78       The worker process will fork a new process for every job that is being
79       processed. This allows for resources such as memory to be returned to
80       the operating system once a job is finished. Perl fork is very fast, so
81       don't worry about the overhead.
82
83         Minion::Worker
84         |- Minion::Job [1]
85         |- Minion::Job [2]
86         +- ...
87
88       By default up to four jobs will be processed in parallel, but that can
89       be changed with configuration options or on demand with signals.
90
91         $ ./myapp.pl minion worker -j 12
92
93       Jobs can be managed right from the command line with
94       Minion::Command::minion::job.
95
96         $ ./myapp.pl minion job
97
98       You can also add an admin ui to your application by loading the plugin
99       Mojolicious::Plugin::Minion::Admin. Just make sure to secure access
100       before making your application publically accessible.
101
102         # Make admin ui available under "/minion"
103         plugin 'Minion::Admin';
104
105       To manage background worker processes with systemd, you can use a unit
106       configuration file like this.
107
108         [Unit]
109         Description=My Mojolicious application workers
110         After=postgresql.service
111
112         [Service]
113         Type=simple
114         ExecStart=/home/sri/myapp/myapp.pl minion worker -m production
115         KillMode=process
116
117         [Install]
118         WantedBy=multi-user.target
119
120       Every job can fail or succeed, but not get lost, the system is
121       eventually consistent and will preserve job results for as long as you
122       like, depending on "remove_after". While individual workers can fail in
123       the middle of processing a job, the system will detect this and ensure
124       that no job is left in an uncertain state, depending on
125       "missing_after".
126

GROWING

128       And as your application grows, you can move tasks into application
129       specific plugins.
130
131         package MyApp::Task::PokeMojo;
132         use Mojo::Base 'Mojolicious::Plugin', -signatures;
133
134         sub register ($self, $app, $config) {
135           $app->minion->add_task(poke_mojo => sub ($job, @args) {
136             $job->app->ua->get('mojolicious.org');
137             $job->app->log->debug('We have poked mojolicious.org for a visitor');
138           });
139         }
140
141         1;
142
143       Which are loaded like any other plugin from your application.
144
145         # Mojolicious
146         $app->plugin('MyApp::Task::PokeMojo');
147
148         # Mojolicious::Lite
149         plugin 'MyApp::Task::PokeMojo';
150

TASK CLASSES

152       For even more flexibility you can also move tasks into dedicated
153       classes. Allowing the use of Perl features such as inheritance and
154       roles. But be aware that support for task classes is still EXPERIMENTAL
155       and might change without warning!
156
157         package MyApp::Task::PokeMojo;
158         use Mojo::Base 'Minion::Job', -signatures;
159
160         sub run ($self, @args) {
161           $self->app->ua->get('mojolicious.org');
162           $self->app->log->debug('We have poked mojolicious.org for a visitor');
163         }
164
165         1;
166
167       Task classes are registered just like any other task with "add_task"
168       and you can even register the same class with multiple names.
169
170         $minion->add_task(poke_mojo => 'MyApp::Task::PokeMojo');
171

EXAMPLES

173       This distribution also contains a great example application you can use
174       for inspiration. The link checker
175       <https://github.com/mojolicious/minion/tree/master/examples/linkcheck>
176       will show you how to integrate background jobs into well-structured
177       Mojolicious applications.
178

EVENTS

180       Minion inherits all events from Mojo::EventEmitter and can emit the
181       following new ones.
182
183   enqueue
184         $minion->on(enqueue => sub ($minion, $id) {
185           ...
186         });
187
188       Emitted after a job has been enqueued, in the process that enqueued it.
189
190         $minion->on(enqueue => sub ($minion, $id) {
191           say "Job $id has been enqueued.";
192         });
193
194   worker
195         $minion->on(worker => sub ($minion, $worker) {
196           ...
197         });
198
199       Emitted in the worker process after it has been created.
200
201         $minion->on(worker => sub ($minion, $worker) {
202           say "Worker $$ started.";
203         });
204

ATTRIBUTES

206       Minion implements the following attributes.
207
208   app
209         my $app = $minion->app;
210         $minion = $minion->app(MyApp->new);
211
212       Application for job queue, defaults to a Mojo::HelloWorld object. Note
213       that this attribute is weakened.
214
215   backend
216         my $backend = $minion->backend;
217         $minion     = $minion->backend(Minion::Backend::Pg->new);
218
219       Backend, usually a Minion::Backend::Pg object.
220
221   backoff
222         my $cb  = $minion->backoff;
223         $minion = $minion->backoff(sub {...});
224
225       A callback used to calculate the delay for automatically retried jobs,
226       defaults to "(retries ** 4) + 15" (15, 16, 31, 96, 271, 640...), which
227       means that roughly 25 attempts can be made in 21 days.
228
229         $minion->backoff(sub ($retries) {
230           return ($retries ** 4) + 15 + int(rand 30);
231         });
232
233   missing_after
234         my $after = $minion->missing_after;
235         $minion   = $minion->missing_after(172800);
236
237       Amount of time in seconds after which workers without a heartbeat will
238       be considered missing and removed from the registry by "repair",
239       defaults to 1800 (30 minutes).
240
241   remove_after
242         my $after = $minion->remove_after;
243         $minion   = $minion->remove_after(86400);
244
245       Amount of time in seconds after which jobs that have reached the state
246       "finished" and have no unresolved dependencies will be removed
247       automatically by "repair", defaults to 172800 (2 days). It is not
248       recommended to set this value below 2 days.
249
250   stuck_after
251         my $after = $minion->stuck_after;
252         $minion   = $minion->stuck_after(86400);
253
254       Amount of time in seconds after which jobs that have not been processed
255       will be considered stuck by "repair" and transition to the "failed"
256       state, defaults to 172800 (2 days).
257
258   tasks
259         my $tasks = $minion->tasks;
260         $minion   = $minion->tasks({foo => sub {...}});
261
262       Registered tasks.
263

METHODS

265       Minion inherits all methods from Mojo::EventEmitter and implements the
266       following new ones.
267
268   add_task
269         $minion = $minion->add_task(foo => sub {...});
270         $minion = $minion->add_task(foo => 'MyApp::Task::Foo');
271
272       Register a task, which can be a closure or a custom Minion::Job
273       subclass. Note that support for custom task classes is EXPERIMENTAL and
274       might change without warning!
275
276         # Job with result
277         $minion->add_task(add => sub ($job, $first, $second) {
278           $job->finish($first + $second);
279         });
280         my $id = $minion->enqueue(add => [1, 1]);
281         my $result = $minion->job($id)->info->{result};
282
283   broadcast
284         my $bool = $minion->broadcast('some_command');
285         my $bool = $minion->broadcast('some_command', [@args]);
286         my $bool = $minion->broadcast('some_command', [@args], [$id1, $id2, $id3]);
287
288       Broadcast remote control command to one or more workers.
289
290         # Broadcast "stop" command to all workers to kill job 10025
291         $minion->broadcast('stop', [10025]);
292
293         # Broadcast "kill" command to all workers to interrupt job 10026
294         $minion->broadcast('kill', ['INT', 10026]);
295
296         # Broadcast "jobs" command to pause worker 23
297         $minion->broadcast('jobs', [0], [23]);
298
299   class_for_task
300         my $class = $minion->class_for_task('foo');
301
302       Return job class for task. Note that this method is EXPERIMENTAL and
303       might change without warning!
304
305   enqueue
306         my $id = $minion->enqueue('foo');
307         my $id = $minion->enqueue(foo => [@args]);
308         my $id = $minion->enqueue(foo => [@args] => {priority => 1});
309
310       Enqueue a new job with "inactive" state. Arguments get serialized by
311       the "backend" (often with Mojo::JSON), so you shouldn't send objects
312       and be careful with binary data, nested data structures with hash and
313       array references are fine though.
314
315       These options are currently available:
316
317       attempts
318           attempts => 25
319
320         Number of times performing this job will be attempted, with a delay
321         based on "backoff" after the first attempt, defaults to 1.
322
323       delay
324           delay => 10
325
326         Delay job for this many seconds (from now), defaults to 0.
327
328       expire
329           expire => 300
330
331         Job is valid for this many seconds (from now) before it expires. Note
332         that this option is EXPERIMENTAL and might change without warning!
333
334       lax
335           lax => 1
336
337         Existing jobs this job depends on may also have transitioned to the
338         "failed" state to allow for it to be processed, defaults to "false".
339         Note that this option is EXPERIMENTAL and might change without
340         warning!
341
342       notes
343           notes => {foo => 'bar', baz => [1, 2, 3]}
344
345         Hash reference with arbitrary metadata for this job that gets
346         serialized by the "backend" (often with Mojo::JSON), so you shouldn't
347         send objects and be careful with binary data, nested data structures
348         with hash and array references are fine though.
349
350       parents
351           parents => [$id1, $id2, $id3]
352
353         One or more existing jobs this job depends on, and that need to have
354         transitioned to the state "finished" before it can be processed.
355
356       priority
357           priority => 5
358
359         Job priority, defaults to 0. Jobs with a higher priority get
360         performed first.
361
362       queue
363           queue => 'important'
364
365         Queue to put job in, defaults to "default".
366
367   foreground
368         my $bool = $minion->foreground($id);
369
370       Retry job in "minion_foreground" queue, then perform it right away with
371       a temporary worker in this process, very useful for debugging.
372
373   guard
374         my $guard = $minion->guard('foo', 3600);
375         my $guard = $minion->guard('foo', 3600, {limit => 20});
376
377       Same as "lock", but returns a scope guard object that automatically
378       releases the lock as soon as the object is destroyed, or "undef" if
379       aquiring the lock failed.
380
381         # Only one job should run at a time (unique job)
382         $minion->add_task(do_unique_stuff => sub ($job, @args) {
383           return $job->finish('Previous job is still active')
384             unless my $guard = $minion->guard('fragile_backend_service', 7200);
385           ...
386         });
387
388         # Only five jobs should run at a time and we try again later if necessary
389         $minion->add_task(do_concurrent_stuff => sub ($job, @args) {
390           return $job->retry({delay => 30})
391             unless my $guard = $minion->guard('some_web_service', 60, {limit => 5});
392           ...
393         });
394
395   history
396         my $history = $minion->history;
397
398       Get history information for job queue.
399
400       These fields are currently available:
401
402       daily
403           daily => [{epoch => 12345, finished_jobs => 95, failed_jobs => 2}, ...]
404
405         Hourly counts for processed jobs from the past day.
406
407   is_locked
408         my $bool = $minion->is_locked('foo');
409
410       Check if a lock with that name is currently active.
411
412   job
413         my $job = $minion->job($id);
414
415       Get Minion::Job object without making any changes to the actual job or
416       return "undef" if job does not exist.
417
418         # Check job state
419         my $state = $minion->job($id)->info->{state};
420
421         # Get job metadata
422         my $progress = $minion->$job($id)->info->{notes}{progress};
423
424         # Get job result
425         my $result = $minion->job($id)->info->{result};
426
427   jobs
428         my $jobs = $minion->jobs;
429         my $jobs = $minion->jobs({states => ['inactive']});
430
431       Return Minion::Iterator object to safely iterate through job
432       information.
433
434         # Iterate through jobs for two tasks
435         my $jobs = $minion->jobs({tasks => ['foo', 'bar']});
436         while (my $info = $jobs->next) {
437           say "$info->{id}: $info->{state}";
438         }
439
440         # Remove all failed jobs from a named queue
441         my $jobs = $minion->jobs({states => ['failed'], queues => ['unimportant']});
442         while (my $info = $jobs->next) {
443           $minion->job($info->{id})->remove;
444         }
445
446         # Count failed jobs for a task
447         say $minion->jobs({states => ['failed'], tasks => ['foo']})->total;
448
449       These options are currently available:
450
451       ids
452           ids => ['23', '24']
453
454         List only jobs with these ids.
455
456       notes
457           notes => ['foo', 'bar']
458
459         List only jobs with one of these notes.
460
461       queues
462           queues => ['important', 'unimportant']
463
464         List only jobs in these queues.
465
466       states
467           states => ['inactive', 'active']
468
469         List only jobs in these states.
470
471       tasks
472           tasks => ['foo', 'bar']
473
474         List only jobs for these tasks.
475
476       These fields are currently available:
477
478       args
479           args => ['foo', 'bar']
480
481         Job arguments.
482
483       attempts
484           attempts => 25
485
486         Number of times performing this job will be attempted.
487
488       children
489           children => ['10026', '10027', '10028']
490
491         Jobs depending on this job.
492
493       created
494           created => 784111777
495
496         Epoch time job was created.
497
498       delayed
499           delayed => 784111777
500
501         Epoch time job was delayed to.
502
503       expires
504           expires => 784111777
505
506         Epoch time job is valid until before it expires.
507
508       finished
509           finished => 784111777
510
511         Epoch time job was finished.
512
513       id
514           id => 10025
515
516         Job id.
517
518       lax
519           lax => 0
520
521         Existing jobs this job depends on may also have failed to allow for
522         it to be processed.
523
524       notes
525           notes => {foo => 'bar', baz => [1, 2, 3]}
526
527         Hash reference with arbitrary metadata for this job.
528
529       parents
530           parents => ['10023', '10024', '10025']
531
532         Jobs this job depends on.
533
534       priority
535           priority => 3
536
537         Job priority.
538
539       queue
540           queue => 'important'
541
542         Queue name.
543
544       result
545           result => 'All went well!'
546
547         Job result.
548
549       retried
550           retried => 784111777
551
552         Epoch time job has been retried.
553
554       retries
555           retries => 3
556
557         Number of times job has been retried.
558
559       started
560           started => 784111777
561
562         Epoch time job was started.
563
564       state
565           state => 'inactive'
566
567         Current job state, usually "active", "failed", "finished" or
568         "inactive".
569
570       task
571           task => 'foo'
572
573         Task name.
574
575       time
576           time => 78411177
577
578         Server time.
579
580       worker
581           worker => '154'
582
583         Id of worker that is processing the job.
584
585   lock
586         my $bool = $minion->lock('foo', 3600);
587         my $bool = $minion->lock('foo', 3600, {limit => 20});
588
589       Try to acquire a named lock that will expire automatically after the
590       given amount of time in seconds. You can release the lock manually with
591       "unlock" to limit concurrency, or let it expire for rate limiting. For
592       convenience you can also use "guard" to release the lock automatically,
593       even if the job failed.
594
595         # Only one job should run at a time (unique job)
596         $minion->add_task(do_unique_stuff => sub ($job, @args) {
597           return $job->finish('Previous job is still active')
598             unless $minion->lock('fragile_backend_service', 7200);
599           ...
600           $minion->unlock('fragile_backend_service');
601         });
602
603         # Only five jobs should run at a time and we wait for our turn
604         $minion->add_task(do_concurrent_stuff => sub ($job, @args) {
605           sleep 1 until $minion->lock('some_web_service', 60, {limit => 5});
606           ...
607           $minion->unlock('some_web_service');
608         });
609
610         # Only a hundred jobs should run per hour and we try again later if necessary
611         $minion->add_task(do_rate_limited_stuff => sub ($job, @args) {
612           return $job->retry({delay => 3600})
613             unless $minion->lock('another_web_service', 3600, {limit => 100});
614           ...
615         });
616
617       An expiration time of 0 can be used to check if a named lock could have
618       been acquired without creating one.
619
620         # Check if the lock "foo" could have been acquired
621         say 'Lock could have been acquired' unless $minion->lock('foo', 0);
622
623       Or to simply check if a named lock already exists you can also use
624       "is_locked".
625
626       These options are currently available:
627
628       limit
629           limit => 20
630
631         Number of shared locks with the same name that can be active at the
632         same time, defaults to 1.
633
634   new
635         my $minion = Minion->new(Pg => 'postgresql://postgres@/test');
636         my $minion = Minion->new(Pg => Mojo::Pg->new);
637
638       Construct a new Minion object.
639
640   perform_jobs
641         $minion->perform_jobs;
642         $minion->perform_jobs({queues => ['important']});
643
644       Perform all jobs with a temporary worker, very useful for testing.
645
646         # Longer version
647         my $worker = $minion->worker;
648         while (my $job = $worker->register->dequeue(0)) { $job->perform }
649         $worker->unregister;
650
651       These options are currently available:
652
653       queues
654           queues => ['important']
655
656         One or more queues to dequeue jobs from, defaults to "default".
657
658   repair
659         $minion = $minion->repair;
660
661       Repair worker registry and job queue if necessary.
662
663   reset
664         $minion = $minion->reset({all => 1});
665
666       Reset job queue.
667
668       These options are currently available:
669
670       all
671           all => 1
672
673         Reset everything.
674
675       locks
676           locks => 1
677
678         Reset only locks.
679
680   result_p
681         my $promise = $minion->result_p($id);
682         my $promise = $minion->result_p($id, {interval => 5});
683
684       Return a Mojo::Promise object for the result of a job. The state
685       "finished" will result in the promise being "fullfilled", and the state
686       "failed" in the promise being "rejected". This operation can be
687       cancelled by resolving the promise manually at any time.
688
689         # Enqueue job and receive the result at some point in the future
690         my $id = $minion->enqueue('foo');
691         $minion->result_p($id)->then(sub ($info) {
692           my $result = ref $info ? $info->{result} : 'Job already removed';
693           say "Finished: $result";
694         })->catch(sub ($info) {
695           say "Failed: $info->{result}";
696         })->wait;
697
698       These options are currently available:
699
700       interval
701           interval => 5
702
703         Polling interval in seconds for checking if the state of the job has
704         changed, defaults to 3.
705
706   stats
707         my $stats = $minion->stats;
708
709       Get statistics for the job queue.
710
711         # Check idle workers
712         my $idle = $minion->stats->{inactive_workers};
713
714       These fields are currently available:
715
716       active_jobs
717           active_jobs => 100
718
719         Number of jobs in "active" state.
720
721       active_locks
722           active_locks => 100
723
724         Number of active named locks.
725
726       active_workers
727           active_workers => 100
728
729         Number of workers that are currently processing a job.
730
731       delayed_jobs
732           delayed_jobs => 100
733
734         Number of jobs in "inactive" state that are scheduled to run at
735         specific time in the future or have unresolved dependencies.
736
737       enqueued_jobs
738           enqueued_jobs => 100000
739
740         Rough estimate of how many jobs have ever been enqueued.
741
742       failed_jobs
743           failed_jobs => 100
744
745         Number of jobs in "failed" state.
746
747       finished_jobs
748           finished_jobs => 100
749
750         Number of jobs in "finished" state.
751
752       inactive_jobs
753           inactive_jobs => 100
754
755         Number of jobs in "inactive" state.
756
757       inactive_workers
758           inactive_workers => 100
759
760         Number of workers that are currently not processing a job.
761
762       uptime
763           uptime => 1000
764
765         Uptime in seconds.
766
767   unlock
768         my $bool = $minion->unlock('foo');
769
770       Release a named lock that has been previously acquired with "lock".
771
772   worker
773         my $worker = $minion->worker;
774
775       Build Minion::Worker object. Note that this method should only be used
776       to implement custom workers.
777
778         # Use the standard worker with all its features
779         my $worker = $minion->worker;
780         $worker->status->{jobs} = 12;
781         $worker->status->{queues} = ['important'];
782         $worker->run;
783
784         # Perform one job manually in a separate process
785         my $worker = $minion->repair->worker->register;
786         my $job    = $worker->dequeue(5);
787         $job->perform;
788         $worker->unregister;
789
790         # Perform one job manually in this process
791         my $worker = $minion->repair->worker->register;
792         my $job    = $worker->dequeue(5);
793         if (my $err = $job->execute) { $job->fail($err) }
794         else                         { $job->finish }
795         $worker->unregister;
796
797         # Build a custom worker performing multiple jobs at the same time
798         my %jobs;
799         my $worker = $minion->repair->worker->register;
800         do {
801           for my $id (keys %jobs) {
802             delete $jobs{$id} if $jobs{$id}->is_finished;
803           }
804           if (keys %jobs >= 4) { sleep 5 }
805           else {
806             my $job = $worker->dequeue(5);
807             $jobs{$job->id} = $job->start if $job;
808           }
809         } while keys %jobs;
810         $worker->unregister;
811
812   workers
813         my $workers = $minion->workers;
814         my $workers = $minion->workers({ids => [2, 3]});
815
816       Return Minion::Iterator object to safely iterate through worker
817       information.
818
819         # Iterate through workers
820         my $workers = $minion->workers;
821         while (my $info = $workers->next) {
822           say "$info->{id}: $info->{host}";
823         }
824
825       These options are currently available:
826
827       ids
828           ids => ['23', '24']
829
830         List only workers with these ids.
831
832       These fields are currently available:
833
834       id
835           id => 22
836
837         Worker id.
838
839       host
840           host => 'localhost'
841
842         Worker host.
843
844       jobs
845           jobs => ['10023', '10024', '10025', '10029']
846
847         Ids of jobs the worker is currently processing.
848
849       notified
850           notified => 784111777
851
852         Epoch time worker sent the last heartbeat.
853
854       pid
855           pid => 12345
856
857         Process id of worker.
858
859       started
860           started => 784111777
861
862         Epoch time worker was started.
863
864       status
865           status => {queues => ['default', 'important']}
866
867         Hash reference with whatever status information the worker would like
868         to share.
869

API

871       This is the class hierarchy of the Minion distribution.
872
873       • Minion
874
875       • Minion::Backend
876
877         • Minion::Backend::Pg
878
879       • Minion::Command::minion
880
881       • Minion::Command::minion::job
882
883       • Minion::Command::minion::worker
884
885       • Minion::Iterator
886
887       • Minion::Job
888
889       • Minion::Worker
890
891       • Mojolicious::Plugin::Minion
892
893       • Mojolicious::Plugin::Minion::Admin
894

BUNDLED FILES

896       The Minion distribution includes a few files with different licenses
897       that have been bundled for internal use.
898
899   Minion Artwork
900         Copyright (C) 2017, Sebastian Riedel.
901
902       Licensed under the CC-SA License, Version 4.0
903       <http://creativecommons.org/licenses/by-sa/4.0>.
904
905   Bootstrap
906         Copyright (C) 2011-2018 The Bootstrap Authors.
907
908       Licensed under the MIT License,
909       <http://creativecommons.org/licenses/MIT>.
910
911   D3.js
912         Copyright (C) 2010-2016, Michael Bostock.
913
914       Licensed under the 3-Clause BSD License,
915       <https://opensource.org/licenses/BSD-3-Clause>.
916
917   epoch.js
918         Copyright (C) 2014 Fastly, Inc.
919
920       Licensed under the MIT License,
921       <http://creativecommons.org/licenses/MIT>.
922
923   Font Awesome
924         Copyright (C) Dave Gandy.
925
926       Licensed under the MIT License,
927       <http://creativecommons.org/licenses/MIT>, and the SIL OFL 1.1,
928       <http://scripts.sil.org/OFL>.
929
930   moment.js
931         Copyright (C) JS Foundation and other contributors.
932
933       Licensed under the MIT License,
934       <http://creativecommons.org/licenses/MIT>.
935
936   popper.js
937         Copyright (C) Federico Zivolo 2017.
938
939       Licensed under the MIT License,
940       <http://creativecommons.org/licenses/MIT>.
941

AUTHOR

943       Sebastian Riedel, "sri@cpan.org".
944

CREDITS

946       In alphabetical order:
947
948         Andrey Khozov
949
950         Andrii Nikitin
951
952         Brian Medley
953
954         Franz Skale
955
956         Hubert "depesz" Lubaczewski
957
958         Joel Berger
959
960         Paul Williams
961
962         Stefan Adams
963
965       Copyright (C) 2014-2020, Sebastian Riedel and others.
966
967       This program is free software, you can redistribute it and/or modify it
968       under the terms of the Artistic License version 2.0.
969

SEE ALSO

971       <https://github.com/mojolicious/minion>, <https://minion.pm>,
972       Mojolicious::Guides, <https://mojolicious.org>.
973
974
975
976perl v5.32.1                      2021-01-27                         Minion(3)
Impressum