Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package perl-Minion for openSUSE:Factory checked in at 2025-10-07 18:25:59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/perl-Minion (Old) and /work/SRC/openSUSE:Factory/.perl-Minion.new.11973 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "perl-Minion" Tue Oct 7 18:25:59 2025 rev:79 rq:1309341 version:11.0.0 Changes: -------- --- /work/SRC/openSUSE:Factory/perl-Minion/perl-Minion.changes 2025-01-01 23:07:51.936355997 +0100 +++ /work/SRC/openSUSE:Factory/.perl-Minion.new.11973/perl-Minion.changes 2025-10-07 18:26:23.824456284 +0200 @@ -1,0 +2,14 @@ +Thu Aug 28 10:36:53 UTC 2025 - Tina Müller <[email protected]> + +- updated to 11.0.0 (11.0) + see /usr/share/doc/packages/perl-Minion/Changes + + 11.0 2025-08-21 + - Minion::Backend::Pg now requires PostgreSQL 13. + - Removed experimetnal status from lax dependency support. + - Added support for task limits. + - Added Minion::Util module. + - Added spare remote control command, this allows workers to be paused again by combining jobs and spare. + - Fixed a problem where remote control commands would not immediately update the worker status. + +------------------------------------------------------------------- Old: ---- Minion-10.31.tar.gz New: ---- Minion-11.0.tar.gz README.md _scmsync.obsinfo build.specials.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ perl-Minion.spec ++++++ --- /var/tmp/diff_new_pack.xXlk0c/_old 2025-10-07 18:26:24.592488652 +0200 +++ /var/tmp/diff_new_pack.xXlk0c/_new 2025-10-07 18:26:24.592488652 +0200 @@ -1,7 +1,7 @@ # # spec file for package perl-Minion # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) 2025 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,22 +18,26 @@ %define cpan_name Minion Name: perl-Minion -Version: 10.310.0 +Version: 11.0.0 Release: 0 -# 10.31 -> normalize -> 10.310.0 -%define cpan_version 10.31 +# 11.0 -> normalize -> 11.0.0 +%define cpan_version 11.0 License: Artistic-2.0 Summary: Job queue URL: https://metacpan.org/release/%{cpan_name} Source0: https://cpan.metacpan.org/authors/id/S/SR/SRI/%{cpan_name}-%{cpan_version}.tar.gz Source1: cpanspec.yml +Source100: README.md BuildArch: noarch BuildRequires: perl BuildRequires: perl-macros BuildRequires: perl(Mojolicious) >= 9.0 -BuildRequires: perl(YAML::XS) >= 0.67 +BuildRequires: perl(YAML::XS) >= 0.670 Requires: perl(Mojolicious) >= 9.0 -Requires: perl(YAML::XS) >= 0.67 +Requires: perl(YAML::XS) >= 0.670 +Provides: perl(LinkCheck) +Provides: perl(LinkCheck::Controller::Links) +Provides: perl(LinkCheck::Task::CheckLinks) Provides: perl(Minion) = %{version} Provides: perl(Minion::Backend) Provides: perl(Minion::Backend::Pg) @@ -42,6 +46,7 @@ Provides: perl(Minion::Command::minion::worker) Provides: perl(Minion::Iterator) Provides: perl(Minion::Job) +Provides: perl(Minion::Util) Provides: perl(Minion::Worker) Provides: perl(Mojolicious::Plugin::Minion) Provides: perl(Mojolicious::Plugin::Minion::Admin) @@ -66,7 +71,7 @@ Take a look at our excellent documentation in Minion::Guide! %prep -%autosetup -n %{cpan_name}-%{cpan_version} +%autosetup -n %{cpan_name}-%{cpan_version} -p1 find . -type f ! -path "*/t/*" ! -name "*.pl" ! -path "*/bin/*" ! -path "*/script/*" ! -path "*/scripts/*" ! -name "configure" -print0 | xargs -0 chmod 644 ++++++ Minion-10.31.tar.gz -> Minion-11.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/Changes new/Minion-11.0/Changes --- old/Minion-10.31/Changes 2024-09-21 15:52:15.000000000 +0200 +++ new/Minion-11.0/Changes 2025-08-22 16:29:56.884570480 +0200 @@ -1,4 +1,12 @@ +11.0 2025-08-21 + - Minion::Backend::Pg now requires PostgreSQL 13. + - Removed experimetnal status from lax dependency support. + - Added support for task limits. + - Added Minion::Util module. + - Added spare remote control command, this allows workers to be paused again by combining jobs and spare. + - Fixed a problem where remote control commands would not immediately update the worker status. + 10.31 2024-09-21 - Restore old repair behavior for job dependencies without performance loss. (HEM42) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/MANIFEST new/Minion-11.0/MANIFEST --- old/Minion-10.31/MANIFEST 2024-09-21 15:52:57.000000000 +0200 +++ new/Minion-11.0/MANIFEST 2025-08-22 16:31:34.808768201 +0200 @@ -21,6 +21,7 @@ lib/Minion/Guide.pod lib/Minion/Iterator.pm lib/Minion/Job.pm +lib/Minion/Util.pm lib/Minion/Worker.pm lib/Mojolicious/Plugin/Minion.pm lib/Mojolicious/Plugin/Minion/Admin.pm @@ -63,6 +64,7 @@ LICENSE Makefile.PL MANIFEST This list of files +MANIFEST.bak MANIFEST.SKIP README.md t/backend.t @@ -79,5 +81,6 @@ t/pg_worker.t t/pod.t t/pod_coverage.t +t/util.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/MANIFEST.bak new/Minion-11.0/MANIFEST.bak --- old/Minion-10.31/MANIFEST.bak 1970-01-01 01:00:00.000000000 +0100 +++ new/Minion-11.0/MANIFEST.bak 2024-09-21 15:52:54.000000000 +0200 @@ -0,0 +1,81 @@ +.perltidyrc +Changes +examples/admin.png +examples/linkcheck/lib/LinkCheck.pm +examples/linkcheck/lib/LinkCheck/Controller/Links.pm +examples/linkcheck/lib/LinkCheck/Task/CheckLinks.pm +examples/linkcheck/linkcheck.conf +examples/linkcheck/script/linkcheck +examples/linkcheck/t/linkcheck.t +examples/linkcheck/templates/layouts/linkcheck.html.ep +examples/linkcheck/templates/links/index.html.ep +examples/linkcheck/templates/links/result.html.ep +examples/minion_bench.pl +lib/Minion.pm +lib/Minion/Backend.pm +lib/Minion/Backend/Pg.pm +lib/Minion/Backend/resources/migrations/pg.sql +lib/Minion/Command/minion.pm +lib/Minion/Command/minion/job.pm +lib/Minion/Command/minion/worker.pm +lib/Minion/Guide.pod +lib/Minion/Iterator.pm +lib/Minion/Job.pm +lib/Minion/Worker.pm +lib/Mojolicious/Plugin/Minion.pm +lib/Mojolicious/Plugin/Minion/Admin.pm +lib/Mojolicious/Plugin/Minion/resources/public/minion/app.css +lib/Mojolicious/Plugin/Minion/resources/public/minion/app.js +lib/Mojolicious/Plugin/Minion/resources/public/minion/bootstrap/bootstrap.css +lib/Mojolicious/Plugin/Minion/resources/public/minion/bootstrap/bootstrap.js +lib/Mojolicious/Plugin/Minion/resources/public/minion/d3/d3.js +lib/Mojolicious/Plugin/Minion/resources/public/minion/epoch/epoch.css +lib/Mojolicious/Plugin/Minion/resources/public/minion/epoch/epoch.js +lib/Mojolicious/Plugin/Minion/resources/public/minion/fontawesome/fontawesome.css +lib/Mojolicious/Plugin/Minion/resources/public/minion/jquery/jquery.js +lib/Mojolicious/Plugin/Minion/resources/public/minion/logo-black-2x.png +lib/Mojolicious/Plugin/Minion/resources/public/minion/logo-black.png +lib/Mojolicious/Plugin/Minion/resources/public/minion/moment/moment.js +lib/Mojolicious/Plugin/Minion/resources/public/minion/pinstripe-light.png +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-brands-400.eot +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-brands-400.svg +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-brands-400.ttf +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-brands-400.woff +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-brands-400.woff2 +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-regular-400.eot +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-regular-400.svg +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-regular-400.ttf +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-regular-400.woff +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-regular-400.woff2 +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-solid-900.eot +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-solid-900.svg +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-solid-900.ttf +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-solid-900.woff +lib/Mojolicious/Plugin/Minion/resources/public/minion/webfonts/fa-solid-900.woff2 +lib/Mojolicious/Plugin/Minion/resources/templates/layouts/minion.html.ep +lib/Mojolicious/Plugin/Minion/resources/templates/minion/_limit.html.ep +lib/Mojolicious/Plugin/Minion/resources/templates/minion/_notifications.html.ep +lib/Mojolicious/Plugin/Minion/resources/templates/minion/_pagination.html.ep +lib/Mojolicious/Plugin/Minion/resources/templates/minion/dashboard.html.ep +lib/Mojolicious/Plugin/Minion/resources/templates/minion/jobs.html.ep +lib/Mojolicious/Plugin/Minion/resources/templates/minion/locks.html.ep +lib/Mojolicious/Plugin/Minion/resources/templates/minion/workers.html.ep +LICENSE +Makefile.PL +MANIFEST This list of files +MANIFEST.SKIP +README.md +t/backend.t +t/commands.t +t/lib/MinionTest/AddTestTask.pm +t/lib/MinionTest/BadTestTask.pm +t/lib/MinionTest/EmptyTestTask.pm +t/lib/MinionTest/FailTestTask.pm +t/lib/MinionTest/NoResultTestTask.pm +t/lib/MinionTest/SyntaxErrorTestTask.pm +t/pg.t +t/pg_admin.t +t/pg_lite_app.t +t/pg_worker.t +t/pod.t +t/pod_coverage.t diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/META.json new/Minion-11.0/META.json --- old/Minion-10.31/META.json 2024-09-21 15:52:57.000000000 +0200 +++ new/Minion-11.0/META.json 2025-08-22 16:31:34.784813091 +0200 @@ -4,7 +4,7 @@ "Sebastian Riedel <[email protected]>" ], "dynamic_config" : 0, - "generated_by" : "ExtUtils::MakeMaker version 7.64, CPAN::Meta::Converter version 2.150010", + "generated_by" : "ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010", "license" : [ "artistic_2" ], @@ -58,6 +58,6 @@ "web" : "https://web.libera.chat/#mojo" } }, - "version" : "10.31", - "x_serialization_backend" : "JSON::PP version 4.07" + "version" : "11.0", + "x_serialization_backend" : "JSON::PP version 4.16" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/META.yml new/Minion-11.0/META.yml --- old/Minion-10.31/META.yml 2024-09-21 15:52:56.000000000 +0200 +++ new/Minion-11.0/META.yml 2025-08-22 16:31:34.666269415 +0200 @@ -7,7 +7,7 @@ configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 -generated_by: 'ExtUtils::MakeMaker version 7.64, CPAN::Meta::Converter version 2.150010' +generated_by: 'ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010' license: artistic_2 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html @@ -30,5 +30,5 @@ homepage: https://mojolicious.org license: http://www.opensource.org/licenses/artistic-license-2.0 repository: https://github.com/mojolicious/minion.git -version: '10.31' +version: '11.0' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/examples/linkcheck/t/linkcheck.t new/Minion-11.0/examples/linkcheck/t/linkcheck.t --- old/Minion-10.31/examples/linkcheck/t/linkcheck.t 2023-04-12 14:32:58.000000000 +0200 +++ new/Minion-11.0/examples/linkcheck/t/linkcheck.t 2025-05-05 12:02:09.122123095 +0200 @@ -24,11 +24,17 @@ # Enqueue a background job $t->get_ok('/')->status_is(200)->text_is('title' => 'Check links')->element_exists('form input[type=url]'); -$t->post_ok('/links' => form => {url => 'https://mojolicious.org'})->status_is(200)->text_is('title' => 'Result') - ->text_is('p' => 'Waiting for result...')->element_exists_not('table'); +$t->post_ok('/links' => form => {url => 'https://mojolicious.org'}) + ->status_is(200) + ->text_is('title' => 'Result') + ->text_is('p' => 'Waiting for result...') + ->element_exists_not('table'); # Perform the background job -$t->get_ok('/links/1')->status_is(200)->text_is('title' => 'Result')->text_is('p' => 'Waiting for result...') +$t->get_ok('/links/1') + ->status_is(200) + ->text_is('title' => 'Result') + ->text_is('p' => 'Waiting for result...') ->element_exists_not('table'); $t->app->minion->perform_jobs; $t->get_ok('/links/1')->status_is(200)->text_is('title' => 'Result')->element_exists_not('p')->element_exists('table'); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/examples/minion_bench.pl new/Minion-11.0/examples/minion_bench.pl --- old/Minion-10.31/examples/minion_bench.pl 2022-07-21 01:39:53.000000000 +0200 +++ new/Minion-11.0/examples/minion_bench.pl 2025-05-06 14:32:39.493359401 +0200 @@ -3,18 +3,20 @@ use Minion; use Time::HiRes 'time'; -my $ENQUEUE = 10000; -my $DEQUEUE = 1000; +my $ENQUEUE = 20000; +my $BATCHES = 20; +my $DEQUEUE = 50; +my $PARENTS = 50; +my $CHILDREN = 500; my $REPETITIONS = 2; my $WORKERS = 4; my $INFO = 100; my $STATS = 100; -my $REPAIR = 100; +my $REPAIR = 10; my $LOCK = 1000; my $UNLOCK = 1000; -# A benchmark script for comparing backends and evaluating the performance -# impact of proposed changes +# A benchmark script for comparing backends and evaluating the performance impact of proposed changes my $minion = Minion->new(Pg => 'postgresql://[email protected]:5432/postgres'); $minion->add_task(foo => sub { }); $minion->add_task(bar => sub { }); @@ -22,14 +24,20 @@ # Enqueue say "Clean start with $ENQUEUE jobs"; -my @parents = map { $minion->enqueue('foo') } 1 .. 5; +my @parents = map { $minion->enqueue('foo') } 1 .. $PARENTS; +my $worker = $minion->worker->register; +$worker->dequeue(0.5, {id => $_})->finish for @parents; +$worker->unregister; my $before = time; -$minion->enqueue($_ % 2 ? 'foo' : 'bar' => [] => {parents => \@parents}) for 1 .. $ENQUEUE; +my @jobs = map { $minion->enqueue($_ % 2 ? 'foo' : 'bar' => [] => {parents => \@parents}) } 1 .. $ENQUEUE; my $elapsed = time - $before; my $avg = sprintf '%.3f', $ENQUEUE / $elapsed; say "Enqueued $ENQUEUE jobs in $elapsed seconds ($avg/s)"; +$minion->enqueue('foo' => [] => {parents => \@jobs}) for 1 .. $CHILDREN; $minion->backend->pg->db->query('ANALYZE minion_jobs'); +my $FINISH = $DEQUEUE * $BATCHES; + # Dequeue sub dequeue { my @pids; @@ -37,12 +45,18 @@ die "Couldn't fork: $!" unless defined(my $pid = fork); unless ($pid) { my $worker = $minion->repair->worker->register; - say "$$ will finish $DEQUEUE jobs"; my $before = time; - $worker->dequeue(0.5)->finish for 1 .. $DEQUEUE; + for (1 .. $BATCHES) { + my @jobs; + while (@jobs < $DEQUEUE) { + next unless my $job = $worker->dequeue(0.5); + push @jobs, $job; + } + $_->finish({minion_bench => $_->id}) for @jobs; + } my $elapsed = time - $before; - my $avg = sprintf '%.3f', $DEQUEUE / $elapsed; - say "$$ finished $DEQUEUE jobs in $elapsed seconds ($avg/s)"; + my $avg = sprintf '%.3f', $FINISH / $elapsed; + say "$$ finished $FINISH jobs in $elapsed seconds ($avg/s)"; $worker->unregister; exit; } @@ -53,8 +67,8 @@ my $before = time; waitpid $_, 0 for @pids; my $elapsed = time - $before; - my $avg = sprintf '%.3f', ($DEQUEUE * $WORKERS) / $elapsed; - say "$WORKERS workers finished $DEQUEUE jobs each in $elapsed seconds ($avg/s)"; + my $avg = sprintf '%.3f', ($FINISH * $WORKERS) / $elapsed; + say "$WORKERS workers finished $FINISH jobs each in $elapsed seconds ($avg/s)"; } dequeue() for 1 .. $REPETITIONS; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/lib/Minion/Backend/Pg.pm new/Minion-11.0/lib/Minion/Backend/Pg.pm --- old/Minion-10.31/lib/Minion/Backend/Pg.pm 2024-09-21 15:49:15.000000000 +0200 +++ new/Minion-11.0/lib/Minion/Backend/Pg.pm 2025-08-22 16:26:17.752364800 +0200 @@ -128,7 +128,7 @@ my $self = shift->SUPER::new(pg => Mojo::Pg->new(@_)); my $db = Mojo::Pg->new(@_)->db; - croak 'PostgreSQL 9.5 or later is required' if $db->dbh->{pg_server_version} < 90500; + croak 'PostgreSQL 12 or later is required' if $db->dbh->{pg_server_version} < 130000; $db->disconnect; my $schema = path(__FILE__)->dirname->child('resources', 'migrations', 'pg.sql'); @@ -267,21 +267,25 @@ my ($self, $id, $options) = @_; return $self->pg->db->query( - q{UPDATE minion_jobs SET started = NOW(), state = 'active', worker = ? - WHERE id = ( + q{WITH cte AS ( SELECT id FROM minion_jobs AS j WHERE delayed <= NOW() AND id = COALESCE(?, id) AND (parents = '{}' OR NOT EXISTS ( - SELECT 1 FROM minion_jobs WHERE id = ANY (j.parents) AND ( + SELECT 1 FROM minion_jobs + WHERE id = ANY (j.parents) AND ( state = 'active' OR (state = 'failed' AND NOT j.lax) - OR (state = 'inactive' AND (expires IS NULL OR expires > NOW()))) + OR (state = 'inactive' AND (expires IS NULL OR expires > NOW())) + ) )) AND priority >= COALESCE(?, priority) AND queue = ANY (?) AND state = 'inactive' AND task = ANY (?) - AND (EXPIRES IS NULL OR expires > NOW()) + AND (expires IS NULL OR expires > NOW()) ORDER BY priority DESC, id LIMIT 1 FOR UPDATE SKIP LOCKED ) - RETURNING id, args, retries, task}, $id, $options->{id}, $options->{min_priority}, - $options->{queues} || ['default'], [keys %{$self->minion->tasks}] + UPDATE minion_jobs + SET started = NOW(), state = 'active', worker = ? + WHERE id = (SELECT id FROM cte) + RETURNING id, args, retries, task}, $options->{id}, $options->{min_priority}, $options->{queues} || ['default'], + $options->{tasks} || [keys %{$self->minion->tasks}], $id )->expand->hash; } @@ -370,6 +374,12 @@ One or more queues to dequeue jobs from, defaults to C<default>. +=item tasks + + tasks => ['foo', 'bar'] + +One or more tasks to dequeue jobs for, defaults to all tasks. + =back These fields are currently available: @@ -438,7 +448,7 @@ lax => 1 Existing jobs this job depends on may also have transitioned to the C<failed> state to allow for it to be processed, -defaults to C<false>. Note that this option is B<EXPERIMENTAL> and might change without warning! +defaults to C<false>. =item notes @@ -957,7 +967,7 @@ lax => 1 Existing jobs this job depends on may also have transitioned to the C<failed> state to allow for it to be processed, -defaults to C<false>. Note that this option is B<EXPERIMENTAL> and might change without warning! +defaults to C<false>. =item parents diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/lib/Minion/Backend.pm new/Minion-11.0/lib/Minion/Backend.pm --- old/Minion-10.31/lib/Minion/Backend.pm 2024-03-19 17:25:46.000000000 +0100 +++ new/Minion-11.0/lib/Minion/Backend.pm 2025-08-22 16:25:35.984148164 +0200 @@ -131,6 +131,12 @@ One or more queues to dequeue jobs from, defaults to C<default>. +=item tasks + + tasks => ['foo', 'bar'] + +One or more tasks to dequeue jobs for, defaults to all. + =back These fields are currently available: @@ -199,7 +205,7 @@ lax => 1 Existing jobs this job depends on may also have transitioned to the C<failed> state to allow for it to be processed, -defaults to C<false>. Note that this option is B<EXPERIMENTAL> and might change without warning! +defaults to C<false>. =item notes @@ -714,7 +720,7 @@ lax => 1 Existing jobs this job depends on may also have transitioned to the C<failed> state to allow for it to be processed, -defaults to C<false>. Note that this option is B<EXPERIMENTAL> and might change without warning! +defaults to C<false>. =item parents diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/lib/Minion/Command/minion/worker.pm new/Minion-11.0/lib/Minion/Command/minion/worker.pm --- old/Minion-10.31/lib/Minion/Command/minion/worker.pm 2022-07-21 01:39:53.000000000 +0200 +++ new/Minion-11.0/lib/Minion/Command/minion/worker.pm 2025-08-22 14:49:49.998709294 +0200 @@ -16,7 +16,8 @@ 'D|dequeue-timeout=i' => \$status->{dequeue_timeout}, 'I|heartbeat-interval=i' => \$status->{heartbeat_interval}, 'j|jobs=i' => \$status->{jobs}, - 'q|queue=s' => \my @queues, + 'L|limit=s' => sub { $_[1] =~ /^(\S+)=(\d+)$/ ? ($status->{limits}{$1} = $2) : warn "Invalid limit option: $_[1]" }, + 'q|queue=s' => \my @queues, 'R|repair-interval=i' => \$status->{repair_interval}, 's|spare=i' => \$status->{spare}, 'S|spare-min-priority=i' => \$status->{spare_min_priority}; @@ -50,6 +51,7 @@ ./myapp.pl minion worker ./myapp.pl minion worker -m production -I 15 -C 5 -R 3600 -j 10 ./myapp.pl minion worker -q important -q default + ./myapp.pl minion worker -L foo=2 -L bar=5 Options: -C, --command-interval <seconds> Worker remote control command interval, @@ -65,6 +67,8 @@ parallel in forked worker processes (not including spare processes), defaults to 4 + -L, --limit <name>=<number> Limit number of jobs for a specific + task -m, --mode <name> Operating mode for your application, defaults to the value of MOJO_MODE/PLACK_ENV or "development" @@ -122,7 +126,7 @@ Instruct one or more workers to change the number of jobs to perform concurrently. Setting this value to C<0> will effectively pause the worker. That means all current jobs will be finished, but no new ones accepted, until the number -is increased again. +is increased again. Note that L</"spare"> might need to be adjusted if high priority jobs should be paused as well. =head2 kill @@ -132,6 +136,15 @@ Instruct one or more workers to send a signal to a job that is currently being performed. This command will be ignored by workers that do not have a job matching the id. That means it is safe to broadcast this command to all workers. +=head2 spare + + $ ./myapp.pl minion job -b spare -a '[1]' + $ ./myapp.pl minion job -b spare -a '[1]' 23 + +Instruct one or more workers to change the number of spare worker processes to reserve for high priority jobs. Setting +this value to C<0> will effectively disable the feature, this is required to pause the worker. That means all current +high priority jobs will be finished, but no new ones accepted, until the number is increased again. + =head2 stop $ ./myapp.pl minion job -b stop -a '[10025]' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/lib/Minion/Job.pm new/Minion-11.0/lib/Minion/Job.pm --- old/Minion-10.31/lib/Minion/Job.pm 2023-11-10 15:51:58.000000000 +0100 +++ new/Minion-11.0/lib/Minion/Job.pm 2025-08-22 16:25:43.890419404 +0200 @@ -528,7 +528,7 @@ lax => 1 Existing jobs this job depends on may also have transitioned to the C<failed> state to allow for it to be processed, -defaults to C<false>. Note that this option is B<EXPERIMENTAL> and might change without warning! +defaults to C<false>. =item parents diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/lib/Minion/Util.pm new/Minion-11.0/lib/Minion/Util.pm --- old/Minion-10.31/lib/Minion/Util.pm 1970-01-01 01:00:00.000000000 +0100 +++ new/Minion-11.0/lib/Minion/Util.pm 2025-08-21 14:16:32.144547014 +0200 @@ -0,0 +1,56 @@ +package Minion::Util; +use Mojo::Base -strict; + +use Exporter qw(import); +our @EXPORT_OK = qw(desired_tasks); + +sub desired_tasks { + my ($limits, $available_tasks, $active_tasks) = @_; + + my %count; + $count{$_}++ for @$active_tasks; + + my @desired; + for my $task (@$available_tasks) { + my $count = $count{$task} // 0; + my $limit = $limits->{$task}; + push @desired, $task if !defined($limit) || $count < $limit; + } + + return \@desired; +} + +1; + +=encoding utf8 + +=head1 NAME + +Minion::Util - Minion utility functions + +=head1 SYNOPSIS + + use Minion::Util qw(desired_tasks); + +=head1 DESCRIPTION + +L<Minion::Util> provides utility functions for L<Minion>. + +=head1 FUNCTIONS + +L<Minion::Util> implements the following functions, which can be imported individually. + +=head2 desired_tasks + + my $desired_tasks = desired_trasks $limits, $available_tasks, $active_tasks; + +Enforce limits and generate list of currently desired tasks. + + # ['bar'] + desired_trasks {foo => 2}, ['foo', 'bar'], ['foo', 'foo']; + +=head1 SEE ALSO + +L<Minion>, L<Minion::Guide>, L<https://minion.pm>, L<Mojolicious::Guides>, L<https://mojolicious.org>. + +=cut diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/lib/Minion/Worker.pm new/Minion-11.0/lib/Minion/Worker.pm --- old/Minion-10.31/lib/Minion/Worker.pm 2023-02-04 01:44:47.000000000 +0100 +++ new/Minion-11.0/lib/Minion/Worker.pm 2025-08-22 15:31:35.908372811 +0200 @@ -1,8 +1,9 @@ package Minion::Worker; use Mojo::Base 'Mojo::EventEmitter'; -use Carp qw(croak); -use Mojo::Util qw(steady_time); +use Carp qw(croak); +use Minion::Util qw(desired_tasks); +use Mojo::Util qw(steady_time); has [qw(commands status)] => sub { {} }; has [qw(id minion)]; @@ -34,10 +35,13 @@ sub process_commands { my $self = shift; + my $called = 0; for my $command (@{$self->minion->backend->receive($self->id)}) { next unless my $cb = $self->commands->{shift @$command}; $self->$cb(@$command); + $called++; } + $self->register if $called; return $self; } @@ -56,6 +60,7 @@ $status->{dequeue_timeout} //= 5; $status->{heartbeat_interval} //= 300; $status->{jobs} //= 4; + $status->{limits} //= {}; $status->{queues} ||= ['default']; $status->{performed} //= 0; $status->{repair_interval} //= 21600; @@ -74,9 +79,10 @@ # Remote control commands need to validate arguments carefully my $commands = $self->commands; - local $commands->{jobs} = sub { $status->{jobs} = $_[1] if ($_[1] // '') =~ /^\d+$/ }; - local $commands->{kill} = \&_kill; - local $commands->{stop} = sub { $self->_kill('KILL', $_[1]) }; + local $commands->{jobs} = sub { $status->{jobs} = $_[1] if ($_[1] // '') =~ /^\d+$/ }; + local $commands->{kill} = \&_kill; + local $commands->{spare} = sub { $status->{spare} = $_[1] if ($_[1] // '') =~ /^\d+$/ }; + local $commands->{stop} = sub { $self->_kill('KILL', $_[1]) }; eval { $self->_work until $self->{finished} && !@{$self->{jobs}} }; my $err = $@; @@ -127,8 +133,10 @@ elsif ($status->{jobs} <= @$jobs) { @extra = (min_priority => $status->{spare_min_priority}) } # Try to get more jobs + my $tasks = desired_tasks($status->{limits}, [keys %{$self->minion->tasks}], [map { $_->task } @$jobs]); + return unless @$tasks; my ($max, $queues) = @{$status}{qw(dequeue_timeout queues)}; - my $job = $self->emit('wait')->dequeue($max => {queues => $queues, @extra}); + my $job = $self->emit('wait')->dequeue($max => {queues => $queues, tasks => $tasks, @extra}); push @$jobs, $job->start if $job; } @@ -294,6 +302,12 @@ One or more queues to dequeue jobs from, defaults to C<default>. +=item tasks + + tasks => ['foo', 'bar'] + +One or more tasks to dequeue jobs from, defaults to all tasks. + =back =head2 info diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/lib/Minion.pm new/Minion-11.0/lib/Minion.pm --- old/Minion-10.31/lib/Minion.pm 2024-09-21 15:52:34.000000000 +0200 +++ new/Minion-11.0/lib/Minion.pm 2025-08-22 16:30:08.470564259 +0200 @@ -21,7 +21,7 @@ has [qw(remove_after stuck_after)] => 172800; has tasks => sub { {} }; -our $VERSION = '10.31'; +our $VERSION = '11.0'; sub add_task { my ($self, $name, $task) = @_; @@ -370,6 +370,9 @@ # Broadcast "jobs" command to pause worker 23 $minion->broadcast('jobs', [0], [23]); + # Broadcast "spare" command to disable the feature on all workers + $minion->broadcast('spare', [0]); + =head2 class_for_task my $class = $minion->class_for_task('foo'); @@ -414,7 +417,7 @@ lax => 1 Existing jobs this job depends on may also have transitioned to the C<failed> state to allow for it to be processed, -defaults to C<false>. Note that this option is B<EXPERIMENTAL> and might change without warning! +defaults to C<false>. =item notes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/t/pg.t new/Minion-11.0/t/pg.t --- old/Minion-10.31/t/pg.t 2024-09-21 15:49:15.000000000 +0200 +++ new/Minion-11.0/t/pg.t 2025-08-22 14:43:03.416466012 +0200 @@ -750,6 +750,31 @@ $worker->unregister; }; +subtest 'Task limits' => sub { + my $id = $minion->enqueue('foo'); + my $id2 = $minion->enqueue('foo'); + my $id3 = $minion->enqueue('bar'); + my $id4 = $minion->enqueue('baz'); + my $id5 = $minion->enqueue('bar'); + my $worker = $minion->worker->register; + ok my $job = $worker->dequeue(0, {tasks => ['foo', 'bar', 'baz']}), 'job dequeued'; + is $job->id, $id, 'right id'; + ok $job->finish, 'job finished'; + ok $job = $worker->dequeue(0, {tasks => ['bar']}), 'job dequeued'; + is $job->id, $id3, 'right id'; + ok $job->finish, 'job finished'; + ok $job = $worker->dequeue(0, {tasks => ['bar', 'baz']}), 'job dequeued'; + is $job->id, $id4, 'right id'; + ok $job->finish, 'job finished'; + ok $job = $worker->dequeue(0, {tasks => ['bar', 'baz']}), 'job dequeued'; + is $job->id, $id5, 'right id'; + ok $job->finish, 'job finished'; + ok $job = $worker->dequeue(0, {tasks => ['foo', 'bar', 'baz']}), 'job dequeued'; + is $job->id, $id2, 'right id'; + ok $job->finish, 'job finished'; + $worker->unregister; +}; + subtest 'Events' => sub { my ($enqueue, $pid_start, $pid_stop); my ($failed, $finished) = (0, 0); @@ -1407,6 +1432,7 @@ my @commands; $_->add_command(test_id => sub { push @commands, shift->id }) for $worker, $worker2; $worker->add_command(test_args => sub { shift and push @commands, [@_] })->register; + $worker2->add_command(test_status => sub { $_[0]->status->{test} = $_[1] }); ok $minion->broadcast('test_id', [], [$worker->id]), 'sent command'; ok $minion->broadcast('test_id', [], [$worker->id, $worker2->id]), 'sent command'; $worker->process_commands->register; @@ -1417,10 +1443,12 @@ ok $minion->broadcast('test_whatever'), 'sent command'; ok $minion->broadcast('test_args', [23], []), 'sent command'; ok $minion->broadcast('test_args', [1, [2], {3 => 'three'}], [$worker->id]), 'sent command'; + ok $minion->broadcast('test_status', ['works']), 'sent command'; $_->process_commands for $worker, $worker2; is_deeply \@commands, [$worker->id, [23], [1, [2], {3 => 'three'}], $worker2->id], 'right structure'; + is $worker2->status->{test}, 'works', 'right status'; $_->unregister for $worker, $worker2; - ok !$minion->broadcast('test_id', []), 'command not sent'; + ok !$minion->broadcast('test_id'), 'command not sent'; }; subtest 'Single process worker' => sub { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/t/pg_admin.t new/Minion-11.0/t/pg_admin.t --- old/Minion-10.31/t/pg_admin.t 2024-03-19 17:25:46.000000000 +0100 +++ new/Minion-11.0/t/pg_admin.t 2025-05-05 12:02:09.223186419 +0200 @@ -32,23 +32,37 @@ }; subtest 'Stats' => sub { - $t->get_ok('/minion/stats')->status_is(200)->json_is('/active_jobs' => 0)->json_is('/active_locks' => 0) - ->json_is('/active_workers' => 0)->json_is('/delayed_jobs' => 0)->json_is('/enqueued_jobs' => 2) - ->json_is('/failed_jobs' => 0)->json_is('/finished_jobs' => 1)->json_is('/inactive_jobs' => 1) - ->json_is('/inactive_workers' => 0)->json_has('/uptime'); + $t->get_ok('/minion/stats') + ->status_is(200) + ->json_is('/active_jobs' => 0) + ->json_is('/active_locks' => 0) + ->json_is('/active_workers' => 0) + ->json_is('/delayed_jobs' => 0) + ->json_is('/enqueued_jobs' => 2) + ->json_is('/failed_jobs' => 0) + ->json_is('/finished_jobs' => 1) + ->json_is('/inactive_jobs' => 1) + ->json_is('/inactive_workers' => 0) + ->json_has('/uptime'); }; subtest 'Jobs' => sub { - $t->get_ok('/minion/jobs?state=inactive')->status_is(200)->text_like('tbody td a' => qr/$inactive/) + $t->get_ok('/minion/jobs?state=inactive') + ->status_is(200) + ->text_like('tbody td a' => qr/$inactive/) ->text_unlike('tbody td a' => qr/$finished/); - $t->get_ok('/minion/jobs?state=finished')->status_is(200)->text_like('tbody td a' => qr/$finished/) + $t->get_ok('/minion/jobs?state=finished') + ->status_is(200) + ->text_like('tbody td a' => qr/$finished/) ->text_unlike('tbody td a' => qr/$inactive/); }; subtest 'Workers' => sub { $t->get_ok('/minion/workers')->status_is(200)->element_exists_not('tbody td a'); my $worker = app->minion->worker->register; - $t->get_ok('/minion/workers')->status_is(200)->element_exists('tbody td a') + $t->get_ok('/minion/workers') + ->status_is(200) + ->element_exists('tbody td a') ->text_like('tbody td a' => qr/@{[$worker->id]}/); $worker->unregister; $t->get_ok('/minion/workers')->status_is(200)->element_exists_not('tbody td a'); @@ -64,11 +78,15 @@ $t->get_ok('/minion/locks')->status_is(200)->text_like('tbody td#lock_id' => qr/2/); $t->get_ok('/minion/locks?name=foo')->status_is(200)->text_like('tbody td a' => qr/foo/); $t->get_ok('/minion/locks?name=foo')->status_is(200)->text_like('tbody td#lock_id' => qr/1/); - $t->post_ok('/minion/locks?_method=DELETE&name=bar')->status_is(200)->text_like('tbody td a' => qr/foo/) + $t->post_ok('/minion/locks?_method=DELETE&name=bar') + ->status_is(200) + ->text_like('tbody td a' => qr/foo/) ->text_like('.alert-success', qr/All selected named locks released/); is $t->tx->previous->res->code, 302, 'right status'; like $t->tx->previous->res->headers->location, qr/locks/, 'right "Location" value'; - $t->post_ok('/minion/locks?_method=DELETE&name=foo')->status_is(200)->element_exists_not('tbody td a') + $t->post_ok('/minion/locks?_method=DELETE&name=foo') + ->status_is(200) + ->element_exists_not('tbody td a') ->text_like('.alert-success', qr/All selected named locks released/); is $t->tx->previous->res->code, 302, 'right status'; like $t->tx->previous->res->headers->location, qr/locks/, 'right "Location" value'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/t/pg_worker.t new/Minion-11.0/t/pg_worker.t --- old/Minion-10.31/t/pg_worker.t 2023-02-04 01:45:23.000000000 +0100 +++ new/Minion-11.0/t/pg_worker.t 2025-08-22 15:34:13.193964919 +0200 @@ -40,6 +40,43 @@ is_deeply $minion->job($id)->info->{result}, {just => 'works!'}, 'right result'; }; +subtest 'Task limit' => sub { + $minion->add_task( + $_ => sub { + my $job = shift; + $job->finish({just => 'works!'}); + } + ) for qw(foo bar baz); + my $worker = $minion->worker; + $worker->status->{dequeue_timeout} = 0; + $worker->status->{limits} = {foo => 0}; + $worker->status->{jobs} = 1; + $worker->on( + dequeue => sub { + my ($worker, $job) = @_; + return unless $job->task eq 'baz'; + $job->on(reap => sub { kill 'INT', $$ }); + } + ); + my $id = $minion->enqueue('foo'); + my $id2 = $minion->enqueue('bar'); + my $id3 = $minion->enqueue('baz'); + $worker->run; + is_deeply $minion->job($id)->info->{state}, 'inactive', 'right state'; + is_deeply $minion->job($id2)->info->{state}, 'finished', 'right state'; + is_deeply $minion->job($id3)->info->{state}, 'finished', 'right state'; +}; + +subtest 'Remote control commands (pause before shutdown)' => sub { + my $worker = $minion->worker->register; + $minion->broadcast('jobs', [0]); + $minion->broadcast('spare', [0]); + $worker->on(busy => sub { kill 'INT', $$ }); + $worker->run; + is $worker->status->{jobs}, 0, 'jobs updated'; + is $worker->status->{spare}, 0, 'spare updated'; +}; + subtest 'Clean up event loop' => sub { my $timer = 0; Mojo::IOLoop->recurring(0 => sub { $timer++ }); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Minion-10.31/t/util.t new/Minion-11.0/t/util.t --- old/Minion-10.31/t/util.t 1970-01-01 01:00:00.000000000 +0100 +++ new/Minion-11.0/t/util.t 2025-08-21 14:13:04.568393590 +0200 @@ -0,0 +1,16 @@ +use Mojo::Base -strict; + +use Test::More; +use Minion::Util qw(desired_tasks); + +subtest 'desired_tasks' => sub { + is_deeply desired_tasks({}, [], []), [], 'no tasks'; + is_deeply desired_tasks({foo => 2}, ['foo', 'bar'], ['foo', 'foo']), ['bar'], 'right tasks'; + is_deeply desired_tasks({foo => 0}, ['foo', 'bar'], []), ['bar'], 'right tasks'; + is_deeply desired_tasks({foo => 2}, ['foo', 'bar'], ['foo', 'foo', 'foo']), ['bar'], 'right tasks'; + is_deeply desired_tasks({foo => 2, bar => 1}, ['foo', 'bar', 'baz'], ['foo', 'bar', 'foo']), ['baz'], 'right tasks'; + is_deeply desired_tasks({foo => 2, bar => 1}, ['foo', 'bar'], ['foo', 'foo', 'bar']), [], 'no tasks'; + is_deeply desired_tasks({}, ['foo', 'bar'], ['foo', 'foo']), ['foo', 'bar'], 'right tasks'; +}; + +done_testing(); ++++++ README.md ++++++ ## Build Results Current state of perl in openSUSE:Factory is  The current state of perl in the devel project build (devel:languages:perl)  ++++++ _scmsync.obsinfo ++++++ mtime: 1756377413 commit: 47cbf4af8400b6dcb5bf80919799ef40d9b3b1a4f7e42e2b588e6422b8ecf2c7 url: https://src.opensuse.org/perl/perl-Minion.git revision: 47cbf4af8400b6dcb5bf80919799ef40d9b3b1a4f7e42e2b588e6422b8ecf2c7 projectscmsync: https://src.opensuse.org/perl/_ObsPrj ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2025-08-28 17:53:00.000000000 +0200 @@ -0,0 +1 @@ +.osc
