Minion-Backend-SQLite-4.005000755001750001750 013522042164 15437 5ustar00grinnzgrinnz000000000000README100644001750001750 3353713522042164 16433 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005NAME Minion::Backend::SQLite - SQLite backend for Minion job queue SYNOPSIS use Minion::Backend::SQLite; my $backend = Minion::Backend::SQLite->new('sqlite:test.db'); # Minion use Minion; my $minion = Minion->new(SQLite => 'sqlite:test.db'); # Mojolicious (via Mojolicious::Plugin::Minion) $self->plugin(Minion => { SQLite => 'sqlite:test.db' }); # Mojolicious::Lite (via Mojolicious::Plugin::Minion) plugin Minion => { SQLite => 'sqlite:test.db' }; # Share the database connection cache helper sqlite => sub { state $sqlite = Mojo::SQLite->new('sqlite:test.db') }; plugin Minion => { SQLite => app->sqlite }; DESCRIPTION Minion::Backend::SQLite is a backend for Minion based on Mojo::SQLite. All necessary tables will be created automatically with a set of migrations named minion. If no connection string or :temp: is provided, the database will be created in a temporary directory. ATTRIBUTES Minion::Backend::SQLite inherits all attributes from Minion::Backend and implements the following new ones. dequeue_interval my $seconds = $backend->dequeue_interval; $backend = $backend->dequeue_interval($seconds); Interval in seconds between "dequeue" attempts. Defaults to 0.5. sqlite my $sqlite = $backend->sqlite; $backend = $backend->sqlite(Mojo::SQLite->new); Mojo::SQLite object used to store all data. METHODS Minion::Backend::SQLite inherits all methods from Minion::Backend and implements the following new ones. new my $backend = Minion::Backend::SQLite->new; my $backend = Minion::Backend::SQLite->new(':temp:'); my $backend = Minion::Backend::SQLite->new('sqlite:test.db'); my $backend = Minion::Backend::SQLite->new->tap(sub { $_->sqlite->from_filename('C:\\foo\\bar.db') }); my $backend = Minion::Backend::SQLite->new(Mojo::SQLite->new); Construct a new Minion::Backend::SQLite object. broadcast my $bool = $backend->broadcast('some_command'); my $bool = $backend->broadcast('some_command', [@args]); my $bool = $backend->broadcast('some_command', [@args], [$id1, $id2, $id3]); Broadcast remote control command to one or more workers. dequeue my $job_info = $backend->dequeue($worker_id, 0.5); my $job_info = $backend->dequeue($worker_id, 0.5, {queues => ['important']}); Wait a given amount of time in seconds for a job, dequeue it and transition from inactive to active state, or return undef if queues were empty. Jobs will be checked for in intervals defined by "dequeue_interval" until the timeout is reached. These options are currently available: id id => '10023' Dequeue a specific job. queues queues => ['important'] One or more queues to dequeue jobs from, defaults to default. These fields are currently available: args args => ['foo', 'bar'] Job arguments. id id => '10023' Job ID. retries retries => 3 Number of times job has been retried. task task => 'foo' Task name. enqueue my $job_id = $backend->enqueue('foo'); my $job_id = $backend->enqueue(foo => [@args]); my $job_id = $backend->enqueue(foo => [@args] => {priority => 1}); Enqueue a new job with inactive state. These options are currently available: attempts attempts => 25 Number of times performing this job will be attempted, with a delay based on "backoff" in Minion after the first attempt, defaults to 1. delay delay => 10 Delay job for this many seconds (from now). notes notes => {foo => 'bar', baz => [1, 2, 3]} Hash reference with arbitrary metadata for this job. parents parents => [$id1, $id2, $id3] One or more existing jobs this job depends on, and that need to have transitioned to the state finished before it can be processed. priority priority => 5 Job priority, defaults to 0. Jobs with a higher priority get performed first. queue queue => 'important' Queue to put job in, defaults to default. fail_job my $bool = $backend->fail_job($job_id, $retries); my $bool = $backend->fail_job($job_id, $retries, 'Something went wrong!'); my $bool = $backend->fail_job( $job_id, $retries, {msg => 'Something went wrong!'}); Transition from active to failed state with or without a result, and if there are attempts remaining, transition back to inactive with an exponentially increasing delay based on "backoff" in Minion. finish_job my $bool = $backend->finish_job($job_id, $retries); my $bool = $backend->finish_job($job_id, $retries, 'All went well!'); my $bool = $backend->finish_job($job_id, $retries, {msg => 'All went well!'}); Transition from active to finished state with or without a result. history my $history = $backend->history; Get history information for job queue. Note that this method is EXPERIMENTAL and might change without warning! These fields are currently available: daily daily => [{epoch => 12345, finished_jobs => 95, failed_jobs => 2}, ...] Hourly counts for processed jobs from the past day. list_jobs my $results = $backend->list_jobs($offset, $limit); my $results = $backend->list_jobs($offset, $limit, {states => ['inactive']}); Returns the information about jobs in batches. # Get the total number of results (without limit) my $num = $backend->list_jobs(0, 100, {queues => ['important']})->{total}; # Check job state my $results = $backend->list_jobs(0, 1, {ids => [$job_id]}); my $state = $results->{jobs}[0]{state}; # Get job result my $results = $backend->list_jobs(0, 1, {ids => [$job_id]}); my $result = $results->{jobs}[0]{result}; These options are currently available: ids ids => ['23', '24'] List only jobs with these ids. queues queues => ['important', 'unimportant'] List only jobs in these queues. states states => ['inactive', 'active'] List only jobs in these states. tasks tasks => ['foo', 'bar'] List only jobs for these tasks. These fields are currently available: args args => ['foo', 'bar'] Job arguments. attempts attempts => 25 Number of times performing this job will be attempted. children children => ['10026', '10027', '10028'] Jobs depending on this job. created created => 784111777 Epoch time job was created. delayed delayed => 784111777 Epoch time job was delayed to. finished finished => 784111777 Epoch time job was finished. id id => 10025 Job id. notes notes => {foo => 'bar', baz => [1, 2, 3]} Hash reference with arbitrary metadata for this job. parents parents => ['10023', '10024', '10025'] Jobs this job depends on. priority priority => 3 Job priority. queue queue => 'important' Queue name. result result => 'All went well!' Job result. retried retried => 784111777 Epoch time job has been retried. retries retries => 3 Number of times job has been retried. started started => 784111777 Epoch time job was started. state state => 'inactive' Current job state, usually active, failed, finished or inactive. task task => 'foo' Task name. time time => 78411177 Current time. worker worker => '154' Id of worker that is processing the job. list_locks my $results = $backend->list_locks($offset, $limit); my $results = $backend->list_locks($offset, $limit, {names => ['foo']}); Returns information about locks in batches. # Get the total number of results (without limit) my $num = $backend->list_locks(0, 100, {names => ['bar']})->{total}; # Check expiration time my $results = $backend->list_locks(0, 1, {names => ['foo']}); my $expires = $results->{locks}[0]{expires}; These options are currently available: names names => ['foo', 'bar'] List only locks with these names. These fields are currently available: expires expires => 784111777 Epoch time this lock will expire. name name => 'foo' Lock name. list_workers my $results = $backend->list_workers($offset, $limit); my $results = $backend->list_workers($offset, $limit, {ids => [23]}); Returns information about workers in batches. # Get the total number of results (without limit) my $num = $backend->list_workers(0, 100)->{total}; # Check worker host my $results = $backend->list_workers(0, 1, {ids => [$worker_id]}); my $host = $results->{workers}[0]{host}; These options are currently available: ids ids => ['23', '24'] List only workers with these ids. These fields are currently available: id id => 22 Worker id. host host => 'localhost' Worker host. jobs jobs => ['10023', '10024', '10025', '10029'] Ids of jobs the worker is currently processing. notified notified => 784111777 Epoch time worker sent the last heartbeat. pid pid => 12345 Process id of worker. started started => 784111777 Epoch time worker was started. status status => {queues => ['default', 'important']} Hash reference with whatever status information the worker would like to share. lock my $bool = $backend->lock('foo', 3600); my $bool = $backend->lock('foo', 3600, {limit => 20}); Try to acquire a named lock that will expire automatically after the given amount of time in seconds. An expiration time of 0 can be used to check if a named lock already exists without creating one. These options are currently available: limit limit => 20 Number of shared locks with the same name that can be active at the same time, defaults to 1. note my $bool = $backend->note($job_id, {mojo => 'rocks', minion => 'too'}); Change one or more metadata fields for a job. It is currently an error to attempt to set a metadata field with a name containing the characters ., [, or ]. receive my $commands = $backend->receive($worker_id); Receive remote control commands for worker. register_worker my $worker_id = $backend->register_worker; my $worker_id = $backend->register_worker($worker_id); my $worker_id = $backend->register_worker( $worker_id, {status => {queues => ['default', 'important']}}); Register worker or send heartbeat to show that this worker is still alive. These options are currently available: status status => {queues => ['default', 'important']} Hash reference with whatever status information the worker would like to share. remove_job my $bool = $backend->remove_job($job_id); Remove failed, finished or inactive job from queue. repair $backend->repair; Repair worker registry and job queue if necessary. reset $backend->reset; Reset job queue. retry_job my $bool = $backend->retry_job($job_id, $retries); my $bool = $backend->retry_job($job_id, $retries, {delay => 10}); Transition job back to inactive state, already inactive jobs may also be retried to change options. These options are currently available: attempts attempts => 25 Number of times performing this job will be attempted. delay delay => 10 Delay job for this many seconds (from now). parents parents => [$id1, $id2, $id3] Jobs this job depends on. priority priority => 5 Job priority. queue queue => 'important' Queue to put job in. stats my $stats = $backend->stats; Get statistics for the job queue. These fields are currently available: active_jobs active_jobs => 100 Number of jobs in active state. active_locks active_locks => 100 Number of active named locks. active_workers active_workers => 100 Number of workers that are currently processing a job. delayed_jobs delayed_jobs => 100 Number of jobs in inactive state that are scheduled to run at specific time in the future. Note that this field is EXPERIMENTAL and might change without warning! enqueued_jobs enqueued_jobs => 100000 Rough estimate of how many jobs have ever been enqueued. Note that this field is EXPERIMENTAL and might change without warning! failed_jobs failed_jobs => 100 Number of jobs in failed state. finished_jobs finished_jobs => 100 Number of jobs in finished state. inactive_jobs inactive_jobs => 100 Number of jobs in inactive state. inactive_workers inactive_workers => 100 Number of workers that are currently not processing a job. uptime uptime => undef Uptime in seconds. Always undefined for SQLite. unlock my $bool = $backend->unlock('foo'); Release a named lock. unregister_worker $backend->unregister_worker($worker_id); Unregister worker. BUGS Report any issues on the public bugtracker. AUTHOR Dan Book COPYRIGHT AND LICENSE This software is Copyright (c) 2015 by Dan Book. This is free software, licensed under: The Artistic License 2.0 (GPL Compatible) SEE ALSO Minion, Mojo::SQLite Changes100600001750001750 740413522042164 17010 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.0054.005 2019-08-05 11:00:01 EDT - Allow fields to be removed with note method. 4.004 2019-07-09 00:53:20 EDT - Add time field to list_jobs method. 4.003 2019-06-21 18:06:07 EDT - Ignore missing workers for jobs in the minion_foreground named queue to make debugging very slow jobs easier. 4.002 2018-10-03 13:58:15 EDT - Optimize repair query (yrjustice, #15, #16) 4.001 2018-04-21 19:29:24 EDT - Implement EXPERIMENTAL history method used for Minion Admin plugin history graph. 4.000 2018-04-16 14:58:35 EDT - Minion requirement bumped to 9.0. - Replace queue, state, and task options of list_jobs method with queues, states, and tasks options. - Replace name option of list_locks method with names option. - Replace key/value arguments of note method with a hash reference. - Add parents option to retry_job method. - Re-add active_locks stats field. 3.003 2017-12-10 16:00:33 EST - Remove active_locks stats field as it was incorrect. - Fix list_locks to exclude already expired locks. 3.002 2017-12-09 21:42:19 EST - Add list_locks method. - Add active_locks field to stats. 3.001 2017-11-28 21:57:15 EST - Fix condition in dequeue that could lead to calling usleep with a negative time. (toratora, #12) 3.000 2017-11-17 20:20:58 EST - Minion requirement bumped to 8.0. - Remove job_info and worker_info methods. - Support ids option and return total from list_jobs and list_workers methods. - Add uptime field to stats method (always undef for SQLite). 2.004 2017-11-11 16:17:27 EST - Add dequeue_interval attribute and check for jobs in 0.5 second intervals by default. (#10) 2.003 2017-08-07 16:04:18 EDT - Fix tests for rethrown job exceptions in Minion 7.05. 2.002 2017-08-05 12:01:08 EDT - Add id option to dequeue method to support dequeueing a specific job. - Add attempts option to retry_job method. 2.001 2017-07-20 02:40:46 EDT - Bump Mojo::SQLite requirement to 3.000 to support sharing the database connection cache with existing Mojo::SQLite objects. 2.000 2017-06-26 00:42:49 EDT - Add support for rate limiting and unique jobs with lock and unlock methods. - Add support for job metadata with note method, notes option for enqueue method, and notes field in job_info method. 1.000 2017-04-14 14:54:28 EDT - Support retrying active jobs in retry_job. - Support sharing worker status information in register_worker and worker_info. 0.009 2016-12-19 20:34:58 EST - Increase dependency on Mojo::SQLite for memory leak fix 0.008 2016-12-16 22:36:53 EST - Correct ordering of dequeued jobs that are created in the same second. 0.007 2016-09-19 20:30:32 EDT - Add support for worker remote control commands with broadcast and receive methods. - Fix tests for compatibility with Minion 6.0. 0.006 2016-09-06 23:22:20 EDT - Add support for EXPERIMENTAL enqueued_jobs field in stats method. 0.005 2016-07-02 20:46:32 EDT - Add support for EXPERIMENTAL delayed_jobs field in stats method. - Add queue option to list_jobs method. - Add support for job dependencies. - Add parents option to enqueue method. - Add children and parents fields to job_info method. 0.004 2016-03-16 21:52:15 EDT - Bump Mojo::SQLite dependency to 0.020 for JSON1 support - Use JSON1 fields for job args and result - Use new Mojo::SQLite auto_migrate feature - Various optimizations from Minion::Backend::Pg including much faster dequeue 0.003 2015-11-13 20:19:45 EST - Updated to support Minion 4.0 - Allow retry methods to change options for already inactive jobs 0.002 2015-10-30 17:35:29 EDT - Added support for retrying failed jobs automatically in Minion 3.01 0.001 2015-10-28 21:59:55 EDT - First release LICENSE100644001750001750 2151513522042164 16551 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005This software is Copyright (c) 2015 by Dan Book. This is free software, licensed under: The Artistic License 2.0 (GPL Compatible) The Artistic License 2.0 Copyright (c) 2000-2006, The Perl Foundation. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble This license establishes the terms under which a given free software Package may be copied, modified, distributed, and/or redistributed. The intent is that the Copyright Holder maintains some artistic control over the development of that Package while still keeping the Package available as open source and free software. You are always permitted to make arrangements wholly outside of this license directly with the Copyright Holder of a given Package. If the terms of this license do not permit the full use that you propose to make of the Package, you should contact the Copyright Holder and seek a different licensing arrangement. Definitions "Copyright Holder" means the individual(s) or organization(s) named in the copyright notice for the entire Package. "Contributor" means any party that has contributed code or other material to the Package, in accordance with the Copyright Holder's procedures. "You" and "your" means any person who would like to copy, distribute, or modify the Package. "Package" means the collection of files distributed by the Copyright Holder, and derivatives of that collection and/or of those files. A given Package may consist of either the Standard Version, or a Modified Version. "Distribute" means providing a copy of the Package or making it accessible to anyone else, or in the case of a company or organization, to others outside of your company or organization. "Distributor Fee" means any fee that you charge for Distributing this Package or providing support for this Package to another party. It does not mean licensing fees. "Standard Version" refers to the Package if it has not been modified, or has been modified only in ways explicitly requested by the Copyright Holder. "Modified Version" means the Package, if it has been changed, and such changes were not explicitly requested by the Copyright Holder. "Original License" means this Artistic License as Distributed with the Standard Version of the Package, in its current version or as it may be modified by The Perl Foundation in the future. "Source" form means the source code, documentation source, and configuration files for the Package. "Compiled" form means the compiled bytecode, object code, binary, or any other form resulting from mechanical transformation or translation of the Source form. Permission for Use and Modification Without Distribution (1) You are permitted to use the Standard Version and create and use Modified Versions for any purpose without restriction, provided that you do not Distribute the Modified Version. Permissions for Redistribution of the Standard Version (2) You may Distribute verbatim copies of the Source form of the Standard Version of this Package in any medium without restriction, either gratis or for a Distributor Fee, provided that you duplicate all of the original copyright notices and associated disclaimers. At your discretion, such verbatim copies may or may not include a Compiled form of the Package. (3) You may apply any bug fixes, portability changes, and other modifications made available from the Copyright Holder. The resulting Package will still be considered the Standard Version, and as such will be subject to the Original License. Distribution of Modified Versions of the Package as Source (4) You may Distribute your Modified Version as Source (either gratis or for a Distributor Fee, and with or without a Compiled form of the Modified Version) provided that you clearly document how it differs from the Standard Version, including, but not limited to, documenting any non-standard features, executables, or modules, and provided that you do at least ONE of the following: (a) make the Modified Version available to the Copyright Holder of the Standard Version, under the Original License, so that the Copyright Holder may include your modifications in the Standard Version. (b) ensure that installation of your Modified Version does not prevent the user installing or running the Standard Version. In addition, the Modified Version must bear a name that is different from the name of the Standard Version. (c) allow anyone who receives a copy of the Modified Version to make the Source form of the Modified Version available to others under (i) the Original License or (ii) a license that permits the licensee to freely copy, modify and redistribute the Modified Version using the same licensing terms that apply to the copy that the licensee received, and requires that the Source form of the Modified Version, and of any works derived from it, be made freely available in that license fees are prohibited but Distributor Fees are allowed. Distribution of Compiled Forms of the Standard Version or Modified Versions without the Source (5) You may Distribute Compiled forms of the Standard Version without the Source, provided that you include complete instructions on how to get the Source of the Standard Version. Such instructions must be valid at the time of your distribution. If these instructions, at any time while you are carrying out such distribution, become invalid, you must provide new instructions on demand or cease further distribution. If you provide valid instructions or cease distribution within thirty days after you become aware that the instructions are invalid, then you do not forfeit any of your rights under this license. (6) You may Distribute a Modified Version in Compiled form without the Source, provided that you comply with Section 4 with respect to the Source of the Modified Version. Aggregating or Linking the Package (7) You may aggregate the Package (either the Standard Version or Modified Version) with other packages and Distribute the resulting aggregation provided that you do not charge a licensing fee for the Package. Distributor Fees are permitted, and licensing fees for other components in the aggregation are permitted. The terms of this license apply to the use and Distribution of the Standard or Modified Versions as included in the aggregation. (8) You are permitted to link Modified and Standard Versions with other works, to embed the Package in a larger work of your own, or to build stand-alone binary or bytecode versions of applications that include the Package, and Distribute the result without restriction, provided the result does not expose a direct interface to the Package. Items That are Not Considered Part of a Modified Version (9) Works (including, but not limited to, modules and scripts) that merely extend or make use of the Package, do not, by themselves, cause the Package to be a Modified Version. In addition, such works are not considered parts of the Package itself, and are not subject to the terms of this license. General Provisions (10) Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license. (11) If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license. (12) This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder. (13) This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed. (14) Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. INSTALL100644001750001750 450213522042164 16552 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005This is the Perl distribution Minion-Backend-SQLite. Installing Minion-Backend-SQLite is straightforward. ## Installation with cpanm If you have cpanm, you only need one line: % cpanm Minion::Backend::SQLite If it does not have permission to install modules to the current perl, cpanm will automatically set up and install to a local::lib in your home directory. See the local::lib documentation (https://metacpan.org/pod/local::lib) for details on enabling it in your environment. ## Installing with the CPAN shell Alternatively, if your CPAN shell is set up, you should just be able to do: % cpan Minion::Backend::SQLite ## Manual installation As a last resort, you can manually install it. Download the tarball, untar it, install configure prerequisites (see below), then build it: % perl Build.PL % ./Build && ./Build test Then install it: % ./Build install Or the more portable variation: % perl Build.PL % perl Build % perl Build test % perl Build install If your perl is system-managed, you can create a local::lib in your home directory to install modules to. For details, see the local::lib documentation: https://metacpan.org/pod/local::lib The prerequisites of this distribution will also have to be installed manually. The prerequisites are listed in one of the files: `MYMETA.yml` or `MYMETA.json` generated by running the manual build process described above. ## Configure Prerequisites This distribution requires other modules to be installed before this distribution's installer can be run. They can be found under the "configure_requires" key of META.yml or the "{prereqs}{configure}{requires}" key of META.json. ## Other Prerequisites This distribution may require additional modules to be installed after running Build.PL. Look for prerequisites in the following phases: * to run ./Build, PHASE = build * to use the module code itself, PHASE = runtime * to run tests, PHASE = test They can all be found in the "PHASE_requires" key of MYMETA.yml or the "{prereqs}{PHASE}{requires}" key of MYMETA.json. ## Documentation Minion-Backend-SQLite documentation is available as POD. You can run `perldoc` from a shell to read the documentation: % perldoc Minion::Backend::SQLite For more information on installing Perl modules via CPAN, please see: https://www.cpan.org/modules/INSTALL.html cpanfile100644001750001750 42013522042164 17200 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005requires 'perl' => '5.010001'; requires 'List::Util'; requires 'Minion' => '9.0'; requires 'Mojolicious' => '7.29'; requires 'Mojo::SQLite' => '3.000'; requires 'Sys::Hostname'; requires 'Time::HiRes'; suggests 'Mojo::JSON::MaybeXS'; test_requires 'Test::More' => '0.88'; dist.ini100644001750001750 41113522042164 17140 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005name = Minion-Backend-SQLite author = Dan Book license = Artistic_2_0 copyright_holder = Dan Book copyright_year = 2015 [@Author::DBOOK] :version = 0.032 installer = ModuleBuildTiny::Fallback irc = irc://irc.freenode.net/#mojo pod_tests = 1 META.yml100644001750001750 3100313522042164 17006 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005--- abstract: 'SQLite backend for Minion job queue' author: - 'Dan Book ' build_requires: File::Spec: '0' Module::Metadata: '0' Test::More: '0.88' configure_requires: Module::Build::Tiny: '0.034' dynamic_config: 0 generated_by: 'Dist::Zilla version 6.012, CPAN::Meta::Converter version 2.150010' license: artistic_2 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Minion-Backend-SQLite no_index: directory: - eg - examples - inc - share - t - xt provides: Minion::Backend::SQLite: file: lib/Minion/Backend/SQLite.pm version: '4.005' requires: List::Util: '0' Minion: '9.0' Mojo::SQLite: '3.000' Mojolicious: '7.29' Sys::Hostname: '0' Time::HiRes: '0' perl: '5.010001' resources: IRC: irc://irc.freenode.net/#mojo bugtracker: https://github.com/Grinnz/Minion-Backend-SQLite/issues homepage: https://github.com/Grinnz/Minion-Backend-SQLite repository: https://github.com/Grinnz/Minion-Backend-SQLite.git version: '4.005' x_Dist_Zilla: perl: version: '5.030000' plugins: - class: Dist::Zilla::Plugin::GithubMeta name: '@Author::DBOOK/GithubMeta' version: '0.58' - class: Dist::Zilla::Plugin::ReadmeAnyFromPod config: Dist::Zilla::Role::FileWatcher: version: '0.006' name: '@Author::DBOOK/Readme_Github' version: '0.163250' - class: Dist::Zilla::Plugin::GenerateFile name: '@Author::DBOOK/Generate_Contrib' version: '6.012' - class: Dist::Zilla::Plugin::MetaConfig name: '@Author::DBOOK/MetaConfig' version: '6.012' - class: Dist::Zilla::Plugin::MetaProvides::Package config: Dist::Zilla::Plugin::MetaProvides::Package: finder_objects: - class: Dist::Zilla::Plugin::FinderCode name: '@Author::DBOOK/MetaProvides::Package/AUTOVIV/:InstallModulesPM' version: '6.012' include_underscores: 0 Dist::Zilla::Role::MetaProvider::Provider: $Dist::Zilla::Role::MetaProvider::Provider::VERSION: '2.002004' inherit_missing: '1' inherit_version: '1' meta_noindex: '1' Dist::Zilla::Role::ModuleMetadata: Module::Metadata: '1.000036' version: '0.006' name: '@Author::DBOOK/MetaProvides::Package' version: '2.004003' - class: Dist::Zilla::Plugin::Prereqs::FromCPANfile name: '@Author::DBOOK/Prereqs::FromCPANfile' version: '0.08' - class: Dist::Zilla::Plugin::Git::Contributors config: Dist::Zilla::Plugin::Git::Contributors: git_version: 2.20.1 include_authors: 0 include_releaser: 1 order_by: name paths: [] name: '@Author::DBOOK/Git::Contributors' version: '0.035' - class: Dist::Zilla::Plugin::MetaNoIndex name: '@Author::DBOOK/MetaNoIndex' version: '6.012' - class: Dist::Zilla::Plugin::MetaResources name: '@Author::DBOOK/MetaResources' version: '6.012' - class: Dist::Zilla::Plugin::CheckChangesHasContent name: '@Author::DBOOK/CheckChangesHasContent' version: '0.011' - class: Dist::Zilla::Plugin::Git::Check config: Dist::Zilla::Plugin::Git::Check: untracked_files: die Dist::Zilla::Role::Git::DirtyFiles: allow_dirty: - Changes - README.pod - dist.ini allow_dirty_match: [] changelog: Changes Dist::Zilla::Role::Git::Repo: git_version: 2.20.1 repo_root: . name: '@Author::DBOOK/Git::Check' version: '2.046' - class: Dist::Zilla::Plugin::RewriteVersion config: Dist::Zilla::Plugin::RewriteVersion: add_tarball_name: 0 finders: - ':ExecFiles' - ':InstallModules' global: 0 skip_version_provider: 0 name: '@Author::DBOOK/RewriteVersion' version: '0.018' - class: Dist::Zilla::Plugin::NextRelease name: '@Author::DBOOK/NextRelease' version: '6.012' - class: Dist::Zilla::Plugin::CopyFilesFromRelease config: Dist::Zilla::Plugin::CopyFilesFromRelease: filename: - Build.PL - CONTRIBUTING.md - INSTALL - LICENSE - META.json match: [] name: '@Author::DBOOK/CopyFilesFromRelease' version: '0.006' - class: Dist::Zilla::Plugin::Git::Commit config: Dist::Zilla::Plugin::Git::Commit: add_files_in: - / commit_msg: '%v%n%n%c' Dist::Zilla::Role::Git::DirtyFiles: allow_dirty: - Build.PL - CONTRIBUTING.md - Changes - INSTALL - LICENSE - META.json - README.pod - dist.ini allow_dirty_match: [] changelog: Changes Dist::Zilla::Role::Git::Repo: git_version: 2.20.1 repo_root: . Dist::Zilla::Role::Git::StringFormatter: time_zone: local name: '@Author::DBOOK/Git::Commit' version: '2.046' - class: Dist::Zilla::Plugin::Git::Tag config: Dist::Zilla::Plugin::Git::Tag: branch: ~ changelog: Changes signed: 0 tag: '4.005' tag_format: '%v' tag_message: '%v' Dist::Zilla::Role::Git::Repo: git_version: 2.20.1 repo_root: . Dist::Zilla::Role::Git::StringFormatter: time_zone: local name: '@Author::DBOOK/Git::Tag' version: '2.046' - class: Dist::Zilla::Plugin::BumpVersionAfterRelease config: Dist::Zilla::Plugin::BumpVersionAfterRelease: finders: - ':ExecFiles' - ':InstallModules' global: 0 munge_makefile_pl: 0 name: '@Author::DBOOK/BumpVersionAfterRelease' version: '0.018' - class: Dist::Zilla::Plugin::Git::Commit config: Dist::Zilla::Plugin::Git::Commit: add_files_in: [] commit_msg: 'Bump version' Dist::Zilla::Role::Git::DirtyFiles: allow_dirty: - Changes - dist.ini allow_dirty_match: - (?^:^) changelog: Changes Dist::Zilla::Role::Git::Repo: git_version: 2.20.1 repo_root: . Dist::Zilla::Role::Git::StringFormatter: time_zone: local name: '@Author::DBOOK/Commit_Version_Bump' version: '2.046' - class: Dist::Zilla::Plugin::Git::Push config: Dist::Zilla::Plugin::Git::Push: push_to: - origin remotes_must_exist: 1 Dist::Zilla::Role::Git::Repo: git_version: 2.20.1 repo_root: . name: '@Author::DBOOK/Git::Push' version: '2.046' - class: Dist::Zilla::Plugin::PodSyntaxTests name: '@Author::DBOOK/PodSyntaxTests' version: '6.012' - class: Dist::Zilla::Plugin::PodCoverageTests name: '@Author::DBOOK/PodCoverageTests' version: '6.012' - class: Dist::Zilla::Plugin::Test::ReportPrereqs name: '@Author::DBOOK/Test::ReportPrereqs' version: '0.027' - class: Dist::Zilla::Plugin::Git::GatherDir config: Dist::Zilla::Plugin::GatherDir: exclude_filename: - Build.PL - Build.PL - CONTRIBUTING.md - INSTALL - LICENSE - META.json - Makefile.PL exclude_match: [] follow_symlinks: 0 include_dotfiles: 0 prefix: '' prune_directory: [] root: . Dist::Zilla::Plugin::Git::GatherDir: include_untracked: 0 name: '@Author::DBOOK/Git::GatherDir' version: '2.046' - class: Dist::Zilla::Plugin::Regenerate::AfterReleasers config: Dist::Zilla::Plugin::Regenerate::AfterReleasers: plugins: - '@Author::DBOOK/Readme_Github' - '@Author::DBOOK/CopyFilesFromRelease' Dist::Zilla::Role::Regenerator: $Dist::Zilla::Role::Regenerator::VERSION: '0.001001' name: '@Author::DBOOK/Regenerate::AfterReleasers' version: '0.001001' - class: Dist::Zilla::Plugin::PruneCruft name: '@Author::DBOOK/PruneCruft' version: '6.012' - class: Dist::Zilla::Plugin::ManifestSkip name: '@Author::DBOOK/ManifestSkip' version: '6.012' - class: Dist::Zilla::Plugin::MetaYAML name: '@Author::DBOOK/MetaYAML' version: '6.012' - class: Dist::Zilla::Plugin::MetaJSON name: '@Author::DBOOK/MetaJSON' version: '6.012' - class: Dist::Zilla::Plugin::License name: '@Author::DBOOK/License' version: '6.012' - class: Dist::Zilla::Plugin::ReadmeAnyFromPod config: Dist::Zilla::Role::FileWatcher: version: '0.006' name: '@Author::DBOOK/ReadmeAnyFromPod' version: '0.163250' - class: Dist::Zilla::Plugin::ExecDir name: '@Author::DBOOK/ExecDir' version: '6.012' - class: Dist::Zilla::Plugin::ShareDir name: '@Author::DBOOK/ShareDir' version: '6.012' - class: Dist::Zilla::Plugin::ExecDir name: '@Author::DBOOK/ScriptDir' version: '6.012' - class: Dist::Zilla::Plugin::ModuleBuildTiny::Fallback config: Dist::Zilla::Plugin::ModuleBuildTiny::Fallback: mb_version: '0.28' plugins: - class: Dist::Zilla::Plugin::ModuleBuild config: Dist::Zilla::Role::TestRunner: default_jobs: 1 name: 'ModuleBuild, via ModuleBuildTiny::Fallback' version: '6.012' - class: Dist::Zilla::Plugin::ModuleBuildTiny config: Dist::Zilla::Role::TestRunner: default_jobs: 1 name: 'ModuleBuildTiny, via ModuleBuildTiny::Fallback' version: '0.015' Dist::Zilla::Role::TestRunner: default_jobs: 1 name: '@Author::DBOOK/ModuleBuildTiny::Fallback' version: '0.025' - class: Dist::Zilla::Plugin::RunExtraTests config: Dist::Zilla::Role::TestRunner: default_jobs: 1 name: '@Author::DBOOK/RunExtraTests' version: '0.029' - class: Dist::Zilla::Plugin::InstallGuide config: Dist::Zilla::Role::ModuleMetadata: Module::Metadata: '1.000036' version: '0.006' name: '@Author::DBOOK/InstallGuide' version: '1.200013' - class: Dist::Zilla::Plugin::Manifest name: '@Author::DBOOK/Manifest' version: '6.012' - class: Dist::Zilla::Plugin::TestRelease name: '@Author::DBOOK/TestRelease' version: '6.012' - class: Dist::Zilla::Plugin::ConfirmRelease name: '@Author::DBOOK/ConfirmRelease' version: '6.012' - class: Dist::Zilla::Plugin::UploadToCPAN name: '@Author::DBOOK/UploadToCPAN' version: '6.012' - class: Dist::Zilla::Plugin::FinderCode name: ':InstallModules' version: '6.012' - class: Dist::Zilla::Plugin::FinderCode name: ':IncModules' version: '6.012' - class: Dist::Zilla::Plugin::FinderCode name: ':TestFiles' version: '6.012' - class: Dist::Zilla::Plugin::FinderCode name: ':ExtraTestFiles' version: '6.012' - class: Dist::Zilla::Plugin::FinderCode name: ':ExecFiles' version: '6.012' - class: Dist::Zilla::Plugin::FinderCode name: ':PerlExecFiles' version: '6.012' - class: Dist::Zilla::Plugin::FinderCode name: ':ShareFiles' version: '6.012' - class: Dist::Zilla::Plugin::FinderCode name: ':MainModule' version: '6.012' - class: Dist::Zilla::Plugin::FinderCode name: ':AllFiles' version: '6.012' - class: Dist::Zilla::Plugin::FinderCode name: ':NoFiles' version: '6.012' - class: Dist::Zilla::Plugin::FinderCode name: '@Author::DBOOK/MetaProvides::Package/AUTOVIV/:InstallModulesPM' version: '6.012' zilla: class: Dist::Zilla::Dist::Builder config: is_trial: '0' version: '6.012' x_contributors: - 'Dan Book ' - 'yrjustice <43676784+yrjustice@users.noreply.github.com>' x_generated_by_perl: v5.30.0 x_serialization_backend: 'YAML::Tiny version 1.73' MANIFEST100644001750001750 61213522042164 16630 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.012. Build.PL CONTRIBUTING.md Changes INSTALL LICENSE MANIFEST META.json META.yml README cpanfile dist.ini examples/minion_bench.pl lib/Minion/Backend/SQLite.pm t/00-report-prereqs.dd t/00-report-prereqs.t t/sqlite.t xt/author/pod-coverage.t xt/author/pod-syntax.t xt/author/sqlite_admin.t xt/author/sqlite_lite_app.t Build.PL100644001750001750 671313522042164 17023 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005# This Build.PL for Minion-Backend-SQLite was generated by # Dist::Zilla::Plugin::ModuleBuildTiny::Fallback 0.025 use strict; use warnings; my %configure_requires = ( 'Module::Build::Tiny' => '0.034', ); my %errors = map { eval "require $_; $_->VERSION($configure_requires{$_}); 1"; $_ => $@, } keys %configure_requires; if (!grep { $_ } values %errors) { # This section for Minion-Backend-SQLite was generated by Dist::Zilla::Plugin::ModuleBuildTiny 0.015. use strict; use warnings; use 5.010001; # use Module::Build::Tiny 0.034; Module::Build::Tiny::Build_PL(); } else { if (not $ENV{PERL_MB_FALLBACK_SILENCE_WARNING}) { warn <<'EOW' *** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING *** If you're seeing this warning, your toolchain is really, really old* and you'll almost certainly have problems installing CPAN modules from this century. But never fear, dear user, for we have the technology to fix this! If you're using CPAN.pm to install things, then you can upgrade it using: cpan CPAN If you're using CPANPLUS to install things, then you can upgrade it using: cpanp CPANPLUS If you're using cpanminus, you shouldn't be seeing this message in the first place, so please file an issue on github. This public service announcement was brought to you by the Perl Toolchain Gang, the irc.perl.org #toolchain IRC channel, and the number 42. ---- * Alternatively, you are running this file manually, in which case you need to learn to first fulfill all configure requires prerequisites listed in META.yml or META.json -- or use a cpan client to install this distribution. You can also silence this warning for future installations by setting the PERL_MB_FALLBACK_SILENCE_WARNING environment variable, but please don't do that until you fix your toolchain as described above. Errors from configure prereqs: EOW . do { require Data::Dumper; Data::Dumper->new([ \%errors ])->Indent(2)->Terse(1)->Sortkeys(1)->Dump; }; sleep 10 if -t STDIN && (-t STDOUT || !(-f STDOUT || -c STDOUT)); } # This section was automatically generated by Dist::Zilla::Plugin::ModuleBuild v6.012. use strict; use warnings; require Module::Build; Module::Build->VERSION(0.28); my %module_build_args = ( "configure_requires" => { "Module::Build::Tiny" => "0.034" }, "dist_abstract" => "SQLite backend for Minion job queue", "dist_author" => [ "Dan Book " ], "dist_name" => "Minion-Backend-SQLite", "dist_version" => "4.005", "license" => "artistic_2", "module_name" => "Minion::Backend::SQLite", "recursive_test_files" => 1, "requires" => { "List::Util" => 0, "Minion" => "9.0", "Mojo::SQLite" => "3.000", "Mojolicious" => "7.29", "Sys::Hostname" => 0, "Time::HiRes" => 0, "perl" => "5.010001" }, "test_requires" => { "File::Spec" => 0, "Module::Metadata" => 0, "Test::More" => "0.88" } ); my %fallback_build_requires = ( "File::Spec" => 0, "Module::Metadata" => 0, "Test::More" => "0.88" ); unless ( eval { Module::Build->VERSION(0.4004) } ) { delete $module_build_args{test_requires}; $module_build_args{build_requires} = \%fallback_build_requires; } my $build = Module::Build->new(%module_build_args); $build->create_build_script; } META.json100644001750001750 4663413522042164 17176 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005{ "abstract" : "SQLite backend for Minion job queue", "author" : [ "Dan Book " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.012, CPAN::Meta::Converter version 2.150010", "license" : [ "artistic_2" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Minion-Backend-SQLite", "no_index" : { "directory" : [ "eg", "examples", "inc", "share", "t", "xt" ] }, "prereqs" : { "configure" : { "requires" : { "Module::Build::Tiny" : "0.034" } }, "develop" : { "requires" : { "Pod::Coverage::TrustPod" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08" } }, "runtime" : { "requires" : { "List::Util" : "0", "Minion" : "9.0", "Mojo::SQLite" : "3.000", "Mojolicious" : "7.29", "Sys::Hostname" : "0", "Time::HiRes" : "0", "perl" : "5.010001" }, "suggests" : { "Mojo::JSON::MaybeXS" : "0" } }, "test" : { "recommends" : { "CPAN::Meta" : "2.120900" }, "requires" : { "File::Spec" : "0", "Module::Metadata" : "0", "Test::More" : "0.88" } } }, "provides" : { "Minion::Backend::SQLite" : { "file" : "lib/Minion/Backend/SQLite.pm", "version" : "4.005" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/Grinnz/Minion-Backend-SQLite/issues" }, "homepage" : "https://github.com/Grinnz/Minion-Backend-SQLite", "repository" : { "type" : "git", "url" : "https://github.com/Grinnz/Minion-Backend-SQLite.git", "web" : "https://github.com/Grinnz/Minion-Backend-SQLite" }, "x_IRC" : "irc://irc.freenode.net/#mojo" }, "version" : "4.005", "x_Dist_Zilla" : { "perl" : { "version" : "5.030000" }, "plugins" : [ { "class" : "Dist::Zilla::Plugin::GithubMeta", "name" : "@Author::DBOOK/GithubMeta", "version" : "0.58" }, { "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod", "config" : { "Dist::Zilla::Role::FileWatcher" : { "version" : "0.006" } }, "name" : "@Author::DBOOK/Readme_Github", "version" : "0.163250" }, { "class" : "Dist::Zilla::Plugin::GenerateFile", "name" : "@Author::DBOOK/Generate_Contrib", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::MetaConfig", "name" : "@Author::DBOOK/MetaConfig", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::MetaProvides::Package", "config" : { "Dist::Zilla::Plugin::MetaProvides::Package" : { "finder_objects" : [ { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : "@Author::DBOOK/MetaProvides::Package/AUTOVIV/:InstallModulesPM", "version" : "6.012" } ], "include_underscores" : 0 }, "Dist::Zilla::Role::MetaProvider::Provider" : { "$Dist::Zilla::Role::MetaProvider::Provider::VERSION" : "2.002004", "inherit_missing" : 1, "inherit_version" : 1, "meta_noindex" : 1 }, "Dist::Zilla::Role::ModuleMetadata" : { "Module::Metadata" : "1.000036", "version" : "0.006" } }, "name" : "@Author::DBOOK/MetaProvides::Package", "version" : "2.004003" }, { "class" : "Dist::Zilla::Plugin::Prereqs::FromCPANfile", "name" : "@Author::DBOOK/Prereqs::FromCPANfile", "version" : "0.08" }, { "class" : "Dist::Zilla::Plugin::Git::Contributors", "config" : { "Dist::Zilla::Plugin::Git::Contributors" : { "git_version" : "2.20.1", "include_authors" : 0, "include_releaser" : 1, "order_by" : "name", "paths" : [] } }, "name" : "@Author::DBOOK/Git::Contributors", "version" : "0.035" }, { "class" : "Dist::Zilla::Plugin::MetaNoIndex", "name" : "@Author::DBOOK/MetaNoIndex", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::MetaResources", "name" : "@Author::DBOOK/MetaResources", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::CheckChangesHasContent", "name" : "@Author::DBOOK/CheckChangesHasContent", "version" : "0.011" }, { "class" : "Dist::Zilla::Plugin::Git::Check", "config" : { "Dist::Zilla::Plugin::Git::Check" : { "untracked_files" : "die" }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "Changes", "README.pod", "dist.ini" ], "allow_dirty_match" : [], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.20.1", "repo_root" : "." } }, "name" : "@Author::DBOOK/Git::Check", "version" : "2.046" }, { "class" : "Dist::Zilla::Plugin::RewriteVersion", "config" : { "Dist::Zilla::Plugin::RewriteVersion" : { "add_tarball_name" : 0, "finders" : [ ":ExecFiles", ":InstallModules" ], "global" : 0, "skip_version_provider" : 0 } }, "name" : "@Author::DBOOK/RewriteVersion", "version" : "0.018" }, { "class" : "Dist::Zilla::Plugin::NextRelease", "name" : "@Author::DBOOK/NextRelease", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::CopyFilesFromRelease", "config" : { "Dist::Zilla::Plugin::CopyFilesFromRelease" : { "filename" : [ "Build.PL", "CONTRIBUTING.md", "INSTALL", "LICENSE", "META.json" ], "match" : [] } }, "name" : "@Author::DBOOK/CopyFilesFromRelease", "version" : "0.006" }, { "class" : "Dist::Zilla::Plugin::Git::Commit", "config" : { "Dist::Zilla::Plugin::Git::Commit" : { "add_files_in" : [ "/" ], "commit_msg" : "%v%n%n%c" }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "Build.PL", "CONTRIBUTING.md", "Changes", "INSTALL", "LICENSE", "META.json", "README.pod", "dist.ini" ], "allow_dirty_match" : [], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.20.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Author::DBOOK/Git::Commit", "version" : "2.046" }, { "class" : "Dist::Zilla::Plugin::Git::Tag", "config" : { "Dist::Zilla::Plugin::Git::Tag" : { "branch" : null, "changelog" : "Changes", "signed" : 0, "tag" : "4.005", "tag_format" : "%v", "tag_message" : "%v" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.20.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Author::DBOOK/Git::Tag", "version" : "2.046" }, { "class" : "Dist::Zilla::Plugin::BumpVersionAfterRelease", "config" : { "Dist::Zilla::Plugin::BumpVersionAfterRelease" : { "finders" : [ ":ExecFiles", ":InstallModules" ], "global" : 0, "munge_makefile_pl" : 0 } }, "name" : "@Author::DBOOK/BumpVersionAfterRelease", "version" : "0.018" }, { "class" : "Dist::Zilla::Plugin::Git::Commit", "config" : { "Dist::Zilla::Plugin::Git::Commit" : { "add_files_in" : [], "commit_msg" : "Bump version" }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "Changes", "dist.ini" ], "allow_dirty_match" : [ "(?^:^)" ], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.20.1", "repo_root" : "." }, "Dist::Zilla::Role::Git::StringFormatter" : { "time_zone" : "local" } }, "name" : "@Author::DBOOK/Commit_Version_Bump", "version" : "2.046" }, { "class" : "Dist::Zilla::Plugin::Git::Push", "config" : { "Dist::Zilla::Plugin::Git::Push" : { "push_to" : [ "origin" ], "remotes_must_exist" : 1 }, "Dist::Zilla::Role::Git::Repo" : { "git_version" : "2.20.1", "repo_root" : "." } }, "name" : "@Author::DBOOK/Git::Push", "version" : "2.046" }, { "class" : "Dist::Zilla::Plugin::PodSyntaxTests", "name" : "@Author::DBOOK/PodSyntaxTests", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::PodCoverageTests", "name" : "@Author::DBOOK/PodCoverageTests", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::Test::ReportPrereqs", "name" : "@Author::DBOOK/Test::ReportPrereqs", "version" : "0.027" }, { "class" : "Dist::Zilla::Plugin::Git::GatherDir", "config" : { "Dist::Zilla::Plugin::GatherDir" : { "exclude_filename" : [ "Build.PL", "Build.PL", "CONTRIBUTING.md", "INSTALL", "LICENSE", "META.json", "Makefile.PL" ], "exclude_match" : [], "follow_symlinks" : 0, "include_dotfiles" : 0, "prefix" : "", "prune_directory" : [], "root" : "." }, "Dist::Zilla::Plugin::Git::GatherDir" : { "include_untracked" : 0 } }, "name" : "@Author::DBOOK/Git::GatherDir", "version" : "2.046" }, { "class" : "Dist::Zilla::Plugin::Regenerate::AfterReleasers", "config" : { "Dist::Zilla::Plugin::Regenerate::AfterReleasers" : { "plugins" : [ "@Author::DBOOK/Readme_Github", "@Author::DBOOK/CopyFilesFromRelease" ] }, "Dist::Zilla::Role::Regenerator" : { "$Dist::Zilla::Role::Regenerator::VERSION" : "0.001001" } }, "name" : "@Author::DBOOK/Regenerate::AfterReleasers", "version" : "0.001001" }, { "class" : "Dist::Zilla::Plugin::PruneCruft", "name" : "@Author::DBOOK/PruneCruft", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::ManifestSkip", "name" : "@Author::DBOOK/ManifestSkip", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::MetaYAML", "name" : "@Author::DBOOK/MetaYAML", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::MetaJSON", "name" : "@Author::DBOOK/MetaJSON", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::License", "name" : "@Author::DBOOK/License", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::ReadmeAnyFromPod", "config" : { "Dist::Zilla::Role::FileWatcher" : { "version" : "0.006" } }, "name" : "@Author::DBOOK/ReadmeAnyFromPod", "version" : "0.163250" }, { "class" : "Dist::Zilla::Plugin::ExecDir", "name" : "@Author::DBOOK/ExecDir", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::ShareDir", "name" : "@Author::DBOOK/ShareDir", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::ExecDir", "name" : "@Author::DBOOK/ScriptDir", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::ModuleBuildTiny::Fallback", "config" : { "Dist::Zilla::Plugin::ModuleBuildTiny::Fallback" : { "mb_version" : "0.28", "plugins" : [ { "class" : "Dist::Zilla::Plugin::ModuleBuild", "config" : { "Dist::Zilla::Role::TestRunner" : { "default_jobs" : 1 } }, "name" : "ModuleBuild, via ModuleBuildTiny::Fallback", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::ModuleBuildTiny", "config" : { "Dist::Zilla::Role::TestRunner" : { "default_jobs" : 1 } }, "name" : "ModuleBuildTiny, via ModuleBuildTiny::Fallback", "version" : "0.015" } ] }, "Dist::Zilla::Role::TestRunner" : { "default_jobs" : 1 } }, "name" : "@Author::DBOOK/ModuleBuildTiny::Fallback", "version" : "0.025" }, { "class" : "Dist::Zilla::Plugin::RunExtraTests", "config" : { "Dist::Zilla::Role::TestRunner" : { "default_jobs" : 1 } }, "name" : "@Author::DBOOK/RunExtraTests", "version" : "0.029" }, { "class" : "Dist::Zilla::Plugin::InstallGuide", "config" : { "Dist::Zilla::Role::ModuleMetadata" : { "Module::Metadata" : "1.000036", "version" : "0.006" } }, "name" : "@Author::DBOOK/InstallGuide", "version" : "1.200013" }, { "class" : "Dist::Zilla::Plugin::Manifest", "name" : "@Author::DBOOK/Manifest", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::TestRelease", "name" : "@Author::DBOOK/TestRelease", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::ConfirmRelease", "name" : "@Author::DBOOK/ConfirmRelease", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::UploadToCPAN", "name" : "@Author::DBOOK/UploadToCPAN", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":InstallModules", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":IncModules", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":TestFiles", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExtraTestFiles", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExecFiles", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":PerlExecFiles", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ShareFiles", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":MainModule", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":AllFiles", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":NoFiles", "version" : "6.012" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : "@Author::DBOOK/MetaProvides::Package/AUTOVIV/:InstallModulesPM", "version" : "6.012" } ], "zilla" : { "class" : "Dist::Zilla::Dist::Builder", "config" : { "is_trial" : 0 }, "version" : "6.012" } }, "x_contributors" : [ "Dan Book ", "yrjustice <43676784+yrjustice@users.noreply.github.com>" ], "x_generated_by_perl" : "v5.30.0", "x_serialization_backend" : "Cpanel::JSON::XS version 4.12" } t000755001750001750 013522042164 15623 5ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005sqlite.t100644001750001750 11775613522042164 17532 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005/tuse Mojo::Base -strict; BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } use Test::More; use Config; use Minion; use Mojo::IOLoop; use Sys::Hostname 'hostname'; use Time::HiRes qw(time usleep); use constant HAS_PSEUDOFORK => $Config{d_pseudofork}; my $minion = Minion->new('SQLite'); my $worker; SKIP: { skip 'Minion workers do not support fork emulation', 1 if HAS_PSEUDOFORK; # Nothing to repair $worker = $minion->repair->worker; isa_ok $worker->minion->app, 'Mojolicious', 'has default application'; } # end SKIP # Migrate up and down is $minion->backend->sqlite->migrations->active, 8, 'active version is 8'; is $minion->backend->sqlite->migrations->migrate(0)->active, 0, 'active version is 0'; is $minion->backend->sqlite->migrations->migrate->active, 8, 'active version is 8'; my ($id, $host); SKIP: { skip 'Minion workers do not support fork emulation', 7 if HAS_PSEUDOFORK; # Register and unregister $worker->register; like $worker->info->{started}, qr/^[\d.]+$/, 'has timestamp'; my $notified = $worker->info->{notified}; like $notified, qr/^[\d.]+$/, 'has timestamp'; $id = $worker->id; is $worker->register->id, $id, 'same id'; is $worker->unregister->info, undef, 'no information'; $host = hostname; is $worker->register->info->{host}, $host, 'right host'; is $worker->info->{pid}, $$, 'right pid'; is $worker->unregister->info, undef, 'no information'; } # end SKIP my ($worker2, $job); SKIP: { skip 'Minion workers do not support fork emulation', 9 if HAS_PSEUDOFORK; # Repair missing worker $minion->add_task(test => sub { }); $worker2 = $minion->worker->register; isnt $worker2->id, $worker->id, 'new id'; $id = $minion->enqueue('test'); $job = $worker2->dequeue(0); is $job->id, $id, 'right id'; is $worker2->info->{jobs}[0], $job->id, 'right id'; $id = $worker2->id; undef $worker2; is $job->info->{state}, 'active', 'job is still active'; ok !!$minion->backend->list_workers(0, 1, {ids => [$id]})->{workers}[0], 'is registered'; $minion->backend->sqlite->db->query( q{update minion_workers set notified = datetime('now', '-' || ? || ' seconds') where id = ?}, $minion->missing_after + 1, $id ); $minion->repair; ok !$minion->backend->list_workers(0, 1, {ids => [$id]})->{workers}[0], 'not registered'; like $job->info->{finished}, qr/^[\d.]+$/, 'has finished timestamp'; is $job->info->{state}, 'failed', 'job is no longer active'; is $job->info->{result}, 'Worker went away', 'right result'; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 3 if HAS_PSEUDOFORK; # Repair abandoned job $worker->register; $id = $minion->enqueue('test'); $job = $worker->dequeue(0); is $job->id, $id, 'right id'; $worker->unregister; $minion->repair; is $job->info->{state}, 'failed', 'job is no longer active'; is $job->info->{result}, 'Worker went away', 'right result'; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 3 if HAS_PSEUDOFORK; # Repair abandoned job in minion_foreground queue (have to be handled manually) $worker->register; $id = $minion->enqueue('test', [], {queue => 'minion_foreground'}); $job = $worker->dequeue(0, {queues => ['minion_foreground']}); is $job->id, $id, 'right id'; $worker->unregister; $minion->repair; is $job->info->{state}, 'active', 'job is still active'; is $job->info->{result}, undef, 'no result'; } # end SKIP my ($id2, $id3, $finished); SKIP: { skip 'Minion workers do not support fork emulation', 3 if HAS_PSEUDOFORK; # Repair old jobs $worker->register; $id = $minion->enqueue('test'); $id2 = $minion->enqueue('test'); $id3 = $minion->enqueue('test'); $worker->dequeue(0)->perform for 1 .. 3; $finished = $minion->backend->sqlite->db->query( q{select strftime('%s',finished) as finished from minion_jobs where id = ?}, $id2 )->hash->{finished}; $minion->backend->sqlite->db->query( q{update minion_jobs set finished = datetime(?,'unixepoch') where id = ?}, $finished - ($minion->remove_after + 1), $id2); $finished = $minion->backend->sqlite->db->query( q{select strftime('%s',finished) as finished from minion_jobs where id = ?}, $id3 )->hash->{finished}; $minion->backend->sqlite->db->query( q{update minion_jobs set finished = datetime(?,'unixepoch') where id = ?}, $finished - ($minion->remove_after + 1), $id3); $worker->unregister; $minion->repair; ok $minion->job($id), 'job has not been cleaned up'; ok !$minion->job($id2), 'job has been cleaned up'; ok !$minion->job($id3), 'job has been cleaned up'; } # end SKIP my ($results, $batch); SKIP: { skip 'Minion workers do not support fork emulation', 15 if HAS_PSEUDOFORK; # List workers $worker = $minion->worker->register; $worker2 = $minion->worker->status({whatever => 'works!'})->register; $results = $minion->backend->list_workers(0, 10); is $results->{total}, 2, 'two workers total'; $batch = $results->{workers}; ok $batch->[0]{id}, 'has id'; is $batch->[0]{host}, $host, 'right host'; is $batch->[0]{pid}, $$, 'right pid'; like $batch->[0]{started}, qr/^[\d.]+$/, 'has timestamp'; is $batch->[1]{host}, $host, 'right host'; is $batch->[1]{pid}, $$, 'right pid'; ok !$batch->[2], 'no more results'; $results = $minion->backend->list_workers(0, 1); $batch = $results->{workers}; is $results->{total}, 2, 'two workers total'; is $batch->[0]{id}, $worker2->id, 'right id'; is_deeply $batch->[0]{status}, {whatever => 'works!'}, 'right status'; ok !$batch->[1], 'no more results'; $worker2->status({whatever => 'works too!'})->register; $batch = $minion->backend->list_workers(0, 1)->{workers}; is_deeply $batch->[0]{status}, {whatever => 'works too!'}, 'right status'; $batch = $minion->backend->list_workers(1, 1)->{workers}; is $batch->[0]{id}, $worker->id, 'right id'; ok !$batch->[1], 'no more results'; $worker->unregister; $worker2->unregister; } # end SKIP # Exclusive lock ok $minion->lock('foo', 3600), 'locked'; ok !$minion->lock('foo', 3600), 'not locked again'; ok $minion->unlock('foo'), 'unlocked'; ok !$minion->unlock('foo'), 'not unlocked again'; ok $minion->lock('foo', -3600), 'locked'; ok $minion->lock('foo', 3600), 'locked again'; ok !$minion->lock('foo', -3600), 'not locked again'; ok !$minion->lock('foo', 3600), 'not locked again'; ok $minion->unlock('foo'), 'unlocked'; ok !$minion->unlock('foo'), 'not unlocked again'; ok $minion->lock('yada', 3600, {limit => 1}), 'locked'; ok !$minion->lock('yada', 3600, {limit => 1}), 'not locked again'; # Shared lock ok $minion->lock('bar', 3600, {limit => 3}), 'locked'; ok $minion->lock('bar', 3600, {limit => 3}), 'locked again'; ok $minion->lock('bar', -3600, {limit => 3}), 'locked again'; ok $minion->lock('bar', 3600, {limit => 3}), 'locked again'; ok !$minion->lock('bar', 3600, {limit => 2}), 'not locked again'; ok $minion->lock('baz', 3600, {limit => 3}), 'locked'; ok $minion->unlock('bar'), 'unlocked'; ok $minion->lock('bar', 3600, {limit => 3}), 'locked again'; ok $minion->unlock('bar'), 'unlocked again'; ok $minion->unlock('bar'), 'unlocked again'; ok $minion->unlock('bar'), 'unlocked again'; ok !$minion->unlock('bar'), 'not unlocked again'; ok $minion->unlock('baz'), 'unlocked'; ok !$minion->unlock('baz'), 'not unlocked again'; # List locks is $minion->stats->{active_locks}, 1, 'one active lock'; $results = $minion->backend->list_locks(0, 2); is $results->{locks}[0]{name}, 'yada', 'right name'; like $results->{locks}[0]{expires}, qr/^[\d.]+$/, 'expires'; is $results->{locks}[1], undef, 'no more locks'; is $results->{total}, 1, 'one result'; $minion->unlock('yada'); $minion->lock('yada', 3600, {limit => 2}); $minion->lock('test', 3600, {limit => 1}); $minion->lock('yada', 3600, {limit => 2}); is $minion->stats->{active_locks}, 3, 'three active locks'; $results = $minion->backend->list_locks(1, 1); is $results->{locks}[0]{name}, 'test', 'right name'; like $results->{locks}[0]{expires}, qr/^[\d.]+$/, 'expires'; is $results->{locks}[1], undef, 'no more locks'; is $results->{total}, 3, 'three results'; $results = $minion->backend->list_locks(0, 10, {names => ['yada']}); is $results->{locks}[0]{name}, 'yada', 'right name'; like $results->{locks}[0]{expires}, qr/^[\d.]+$/, 'expires'; is $results->{locks}[1]{name}, 'yada', 'right name'; like $results->{locks}[1]{expires}, qr/^[\d.]+$/, 'expires'; is $results->{locks}[2], undef, 'no more locks'; is $results->{total}, 2, 'two results'; $minion->backend->sqlite->db->query( q{update minion_locks set expires = datetime('now', '-1 second') where name = 'yada'}, ); is $minion->backend->list_locks(0, 10, {names => ['yada']})->{total}, 0, 'no results'; $minion->unlock('test'); is $minion->backend->list_locks(0, 10)->{total}, 0, 'no results'; # Lock with guard ok my $guard = $minion->guard('foo', 3600, {limit => 1}), 'locked'; ok !$minion->guard('foo', 3600, {limit => 1}), 'not locked again'; undef $guard; ok $guard = $minion->guard('foo', 3600), 'locked'; ok !$minion->guard('foo', 3600), 'not locked again'; undef $guard; ok $minion->guard('foo', 3600, {limit => 1}), 'locked again'; ok $minion->guard('foo', 3600, {limit => 1}), 'locked again'; ok $guard = $minion->guard('bar', 3600, {limit => 2}), 'locked'; ok my $guard2 = $minion->guard('bar', 0, {limit => 2}), 'locked'; ok my $guard3 = $minion->guard('bar', 3600, {limit => 2}), 'locked'; undef $guard2; ok !$minion->guard('bar', 3600, {limit => 2}), 'not locked again'; undef $guard; undef $guard3; # Reset $minion->reset->repair; ok !$minion->backend->sqlite->db->query( 'select count(id) as count from minion_jobs')->hash->{count}, 'no jobs'; ok !$minion->backend->sqlite->db->query( 'select count(id) as count from minion_locks')->hash->{count}, 'no locks'; ok !$minion->backend->sqlite->db->query( 'select count(id) as count from minion_workers')->hash->{count}, 'no workers'; SKIP: { skip 'Minion workers do not support fork emulation', 2 if HAS_PSEUDOFORK; # Wait for job my $before = time; $worker = $minion->worker->register; is $worker->dequeue(0.5), undef, 'no jobs yet'; ok !!(($before + 0.4) <= time), 'waited for jobs'; $worker->unregister; } # end SKIP my $job2; # Stats $minion->add_task( add => sub { my ($job, $first, $second) = @_; $job->finish({added => $first + $second}); } ); $minion->add_task(fail => sub { die "Intentional failure!\n" }); my $stats = $minion->stats; is $stats->{active_workers}, 0, 'no active workers'; is $stats->{inactive_workers}, 0, 'no inactive workers'; is $stats->{enqueued_jobs}, 0, 'no enqueued jobs'; is $stats->{active_jobs}, 0, 'no active jobs'; is $stats->{failed_jobs}, 0, 'no failed jobs'; is $stats->{finished_jobs}, 0, 'no finished jobs'; is $stats->{inactive_jobs}, 0, 'no inactive jobs'; is $stats->{delayed_jobs}, 0, 'no delayed jobs'; is $stats->{active_locks}, 0, 'no active locks'; is $stats->{uptime}, undef, 'uptime is undefined'; SKIP: { skip 'Minion workers do not support fork emulation', 1 if HAS_PSEUDOFORK; $worker = $minion->worker->register; is $minion->stats->{inactive_workers}, 1, 'one inactive worker'; } # end SKIP $minion->enqueue('fail'); is $minion->stats->{enqueued_jobs}, 1, 'one enqueued job'; $minion->enqueue('fail'); is $minion->stats->{enqueued_jobs}, 2, 'two enqueued jobs'; is $minion->stats->{inactive_jobs}, 2, 'two inactive jobs'; SKIP: { skip 'Minion workers do not support fork emulation', 3 if HAS_PSEUDOFORK; $job = $worker->dequeue(0); $stats = $minion->stats; is $stats->{active_workers}, 1, 'one active worker'; is $stats->{active_jobs}, 1, 'one active job'; is $stats->{inactive_jobs}, 1, 'one inactive job'; } # end SKIP $minion->enqueue('fail'); SKIP: { skip 'Minion workers do not support fork emulation', 18 if HAS_PSEUDOFORK; $job2 = $worker->dequeue(0); $stats = $minion->stats; is $stats->{active_workers}, 1, 'one active worker'; is $stats->{active_jobs}, 2, 'two active jobs'; is $stats->{inactive_jobs}, 1, 'one inactive job'; ok $job2->finish, 'job finished'; ok $job->finish, 'job finished'; is $minion->stats->{finished_jobs}, 2, 'two finished jobs'; $job = $worker->dequeue(0); ok $job->fail, 'job failed'; is $minion->stats->{failed_jobs}, 1, 'one failed job'; ok $job->retry, 'job retried'; is $minion->stats->{failed_jobs}, 0, 'no failed jobs'; ok $worker->dequeue(0)->finish(['works']), 'job finished'; $worker->unregister; $stats = $minion->stats; is $stats->{active_workers}, 0, 'no active workers'; is $stats->{inactive_workers}, 0, 'no inactive workers'; is $stats->{active_jobs}, 0, 'no active jobs'; is $stats->{failed_jobs}, 0, 'no failed jobs'; is $stats->{finished_jobs}, 3, 'three finished jobs'; is $stats->{inactive_jobs}, 0, 'no inactive jobs'; is $stats->{delayed_jobs}, 0, 'no delayed jobs'; } # end SKIP # History SKIP: { skip 'Minion workers do not support fork emulation', 15 if HAS_PSEUDOFORK; $minion->enqueue('fail'); $worker = $minion->worker->register; $job = $worker->dequeue(0); ok $job->fail, 'job failed'; $worker->unregister; my $history = $minion->history; is $#{$history->{daily}}, 23, 'data for 24 hours'; is $history->{daily}[-1]{finished_jobs} + $history->{daily}[-2]{finished_jobs}, 3, 'one failed job in the last hour'; is $history->{daily}[-1]{failed_jobs} + $history->{daily}[-2]{failed_jobs}, 1, 'three finished jobs in the last hour'; is $history->{daily}[0]{finished_jobs}, 0, 'no finished jobs 24 hours ago'; is $history->{daily}[0]{failed_jobs}, 0, 'no failed jobs 24 hours ago'; ok defined $history->{daily}[0]{epoch}, 'has date value'; ok defined $history->{daily}[1]{epoch}, 'has date value'; ok defined $history->{daily}[12]{epoch}, 'has date value'; ok defined $history->{daily}[-1]{epoch}, 'has date value'; $job->remove; } # end SKIP # List jobs $id = $minion->enqueue('add'); $results = $minion->backend->list_jobs(0, 10); $batch = $results->{jobs}; is $results->{total}, 4, 'four jobs total'; ok $batch->[0]{id}, 'has id'; is $batch->[0]{task}, 'add', 'right task'; is $batch->[0]{state}, 'inactive', 'right state'; is $batch->[0]{retries}, 0, 'job has not been retried'; like $batch->[0]{created}, qr/^[\d.]+$/, 'has created timestamp'; is $batch->[1]{task}, 'fail', 'right task'; is_deeply $batch->[1]{args}, [], 'right arguments'; is_deeply $batch->[1]{notes}, {}, 'right metadata'; SKIP: { skip 'Minion workers do not support fork emulation', 2 if HAS_PSEUDOFORK; is_deeply $batch->[1]{result}, ['works'], 'right result'; is $batch->[1]{state}, 'finished', 'right state'; } # end SKIP is $batch->[1]{priority}, 0, 'right priority'; is_deeply $batch->[1]{parents}, [], 'right parents'; is_deeply $batch->[1]{children}, [], 'right children'; SKIP: { skip 'Minion workers do not support fork emulation', 1 if HAS_PSEUDOFORK; is $batch->[1]{retries}, 1, 'job has been retried'; } # end SKIP like $batch->[1]{created}, qr/^[\d.]+$/, 'has created timestamp'; like $batch->[1]{delayed}, qr/^[\d.]+$/, 'has delayed timestamp'; SKIP: { skip 'Minion workers do not support fork emulation', 3 if HAS_PSEUDOFORK; like $batch->[1]{finished}, qr/^[\d.]+$/, 'has finished timestamp'; like $batch->[1]{retried}, qr/^[\d.]+$/, 'has retried timestamp'; like $batch->[1]{started}, qr/^[\d.]+$/, 'has started timestamp'; } # end SKIP is $batch->[2]{task}, 'fail', 'right task'; SKIP: { skip 'Minion workers do not support fork emulation', 1 if HAS_PSEUDOFORK; is $batch->[2]{state}, 'finished', 'right state'; } # end SKIP is $batch->[2]{retries}, 0, 'job has not been retried'; is $batch->[3]{task}, 'fail', 'right task'; SKIP: { skip 'Minion workers do not support fork emulation', 1 if HAS_PSEUDOFORK; is $batch->[3]{state}, 'finished', 'right state'; } # end SKIP is $batch->[3]{retries}, 0, 'job has not been retried'; ok !$batch->[4], 'no more results'; $batch = $minion->backend->list_jobs(0, 10, {states => ['inactive']})->{jobs}; is $batch->[0]{state}, 'inactive', 'right state'; is $batch->[0]{retries}, 0, 'job has not been retried'; SKIP: { skip 'Minion workers do not support fork emulation', 1 if HAS_PSEUDOFORK; ok !$batch->[1], 'no more results'; } # end SKIP $batch = $minion->backend->list_jobs(0, 10, {tasks => ['add']})->{jobs}; is $batch->[0]{task}, 'add', 'right task'; is $batch->[0]{retries}, 0, 'job has not been retried'; ok !$batch->[1], 'no more results'; $batch = $minion->backend->list_jobs(0, 10, {tasks => ['add', 'fail']})->{jobs}; is $batch->[0]{task}, 'add', 'right task'; is $batch->[1]{task}, 'fail', 'right task'; is $batch->[2]{task}, 'fail', 'right task'; is $batch->[3]{task}, 'fail', 'right task'; ok !$batch->[4], 'no more results'; $batch = $minion->backend->list_jobs(0, 10, {queues => ['default']})->{jobs}; is $batch->[0]{queue}, 'default', 'right queue'; is $batch->[1]{queue}, 'default', 'right queue'; is $batch->[2]{queue}, 'default', 'right queue'; is $batch->[3]{queue}, 'default', 'right queue'; ok !$batch->[4], 'no more results'; TODO: { todo_skip 'filtering jobs by notes not yet implemented', 3; $id2 = $minion->enqueue('test' => [] => {notes => {is_test => 1}}); $batch = $minion->backend->list_jobs(0, 10, {notes => ['is_test']})->{jobs}; is $batch->[0]{task}, 'test', 'right task'; ok !$batch->[4], 'no more results'; ok $minion->job($id2)->remove, 'job removed'; } $batch = $minion->backend->list_jobs(0, 10, {queues => ['does_not_exist']})->{jobs}; is_deeply $batch, [], 'no results'; $results = $minion->backend->list_jobs(0, 1); $batch = $results->{jobs}; is $results->{total}, 4, 'four jobs total'; is $batch->[0]{state}, 'inactive', 'right state'; is $batch->[0]{retries}, 0, 'job has not been retried'; ok !$batch->[1], 'no more results'; $batch = $minion->backend->list_jobs(1, 1)->{jobs}; SKIP: { skip 'Minion workers do not support fork emulation', 2 if HAS_PSEUDOFORK; is $batch->[0]{state}, 'finished', 'right state'; is $batch->[0]{retries}, 1, 'job has been retried'; } # end SKIP ok !$batch->[1], 'no more results'; ok $minion->job($id)->remove, 'job removed'; # Enqueue, dequeue and perform is $minion->job(12345), undef, 'job does not exist'; $id = $minion->enqueue(add => [2, 2]); ok $minion->job($id), 'job does exist'; my $info = $minion->job($id)->info; is_deeply $info->{args}, [2, 2], 'right arguments'; is $info->{priority}, 0, 'right priority'; is $info->{state}, 'inactive', 'right state'; SKIP: { skip 'Minion workers do not support fork emulation', 19 if HAS_PSEUDOFORK; $worker = $minion->worker; is $worker->dequeue(0), undef, 'not registered'; ok !$minion->job($id)->info->{started}, 'no started timestamp'; $job = $worker->register->dequeue(0); is $worker->info->{jobs}[0], $job->id, 'right job'; like $job->info->{created}, qr/^[\d.]+$/, 'has created timestamp'; like $job->info->{started}, qr/^[\d.]+$/, 'has started timestamp'; like $job->info->{time}, qr/[\d.]+$/, 'has current time'; is_deeply $job->args, [2, 2], 'right arguments'; is $job->info->{state}, 'active', 'right state'; is $job->task, 'add', 'right task'; is $job->retries, 0, 'job has not been retried'; $id = $job->info->{worker}; is $minion->backend->list_workers(0, 1, {ids => [$id]})->{workers}[0]{pid}, $$, 'right worker'; ok !$job->info->{finished}, 'no finished timestamp'; $job->perform; is $worker->info->{jobs}[0], undef, 'no jobs'; like $job->info->{finished}, qr/^[\d.]+$/, 'has finished timestamp'; is_deeply $job->info->{result}, {added => 4}, 'right result'; is $job->info->{state}, 'finished', 'right state'; $worker->unregister; $job = $minion->job($job->id); is_deeply $job->args, [2, 2], 'right arguments'; is $job->retries, 0, 'job has not been retried'; is $job->info->{state}, 'finished', 'right state'; is $job->task, 'add', 'right task'; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 29 if HAS_PSEUDOFORK; # Retry and remove $id = $minion->enqueue(add => [5, 6]); $job = $worker->register->dequeue(0); is $job->info->{attempts}, 1, 'job will be attempted once'; is $job->info->{retries}, 0, 'job has not been retried'; is $job->id, $id, 'right id'; ok $job->finish, 'job finished'; ok !$worker->dequeue(0), 'no more jobs'; $job = $minion->job($id); ok !$job->info->{retried}, 'no retried timestamp'; ok $job->retry, 'job retried'; like $job->info->{retried}, qr/^[\d.]+$/, 'has retried timestamp'; is $job->info->{state}, 'inactive', 'right state'; is $job->info->{retries}, 1, 'job has been retried once'; $job = $worker->dequeue(0); is $job->retries, 1, 'job has been retried once'; ok $job->retry, 'job retried'; is $job->id, $id, 'right id'; is $job->info->{retries}, 2, 'job has been retried twice'; $job = $worker->dequeue(0); is $job->info->{state}, 'active', 'right state'; ok $job->finish, 'job finished'; ok $job->remove, 'job has been removed'; ok !$job->retry, 'job not retried'; is $job->info, undef, 'no information'; $id = $minion->enqueue(add => [6, 5]); $job = $minion->job($id); is $job->info->{state}, 'inactive', 'right state'; is $job->info->{retries}, 0, 'job has not been retried'; ok $job->retry, 'job retried'; is $job->info->{state}, 'inactive', 'right state'; is $job->info->{retries}, 1, 'job has been retried once'; $job = $worker->dequeue(0); is $job->id, $id, 'right id'; ok $job->fail, 'job failed'; ok $job->remove, 'job has been removed'; is $job->info, undef, 'no information'; $id = $minion->enqueue(add => [5, 5]); $job = $minion->job("$id"); ok $job->remove, 'job has been removed'; $worker->unregister; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 17 if HAS_PSEUDOFORK; # Jobs with priority $minion->enqueue(add => [1, 2]); $id = $minion->enqueue(add => [2, 4], {priority => 1}); $job = $worker->register->dequeue(0); is $job->id, $id, 'right id'; is $job->info->{priority}, 1, 'right priority'; ok $job->finish, 'job finished'; isnt $worker->dequeue(0)->id, $id, 'different id'; $id = $minion->enqueue(add => [2, 5]); $job = $worker->register->dequeue(0); is $job->id, $id, 'right id'; is $job->info->{priority}, 0, 'right priority'; ok $job->finish, 'job finished'; ok $job->retry({priority => 100}), 'job retried with higher priority'; $job = $worker->dequeue(0); is $job->id, $id, 'right id'; is $job->info->{retries}, 1, 'job has been retried once'; is $job->info->{priority}, 100, 'high priority'; ok $job->finish, 'job finished'; ok $job->retry({priority => 0}), 'job retried with lower priority'; $job = $worker->dequeue(0); is $job->id, $id, 'right id'; is $job->info->{retries}, 2, 'job has been retried twice'; is $job->info->{priority}, 0, 'low priority'; ok $job->finish, 'job finished'; $worker->unregister; } # end SKIP # Delayed jobs $id = $minion->enqueue(add => [2, 1] => {delay => 100}); is $minion->stats->{delayed_jobs}, 1, 'one delayed job'; SKIP: { skip 'Minion workers do not support fork emulation', 15 if HAS_PSEUDOFORK; is $worker->register->dequeue(0), undef, 'too early for job'; ok $minion->job($id)->info->{delayed} > time, 'delayed timestamp'; $minion->backend->sqlite->db->query( q{update minion_jobs set delayed = datetime('now','-1 day') where id = ?}, $id); $job = $worker->dequeue(0); is $job->id, $id, 'right id'; like $job->info->{delayed}, qr/^[\d.]+$/, 'has delayed timestamp'; ok $job->finish, 'job finished'; ok $job->retry, 'job retried'; ok $minion->job($id)->info->{delayed} < time, 'no delayed timestamp'; ok $job->remove, 'job removed'; ok !$job->retry, 'job not retried'; $id = $minion->enqueue(add => [6, 9]); $job = $worker->dequeue(0); ok $job->info->{delayed} < time, 'no delayed timestamp'; ok $job->fail, 'job failed'; ok $job->retry({delay => 100}), 'job retried with delay'; is $job->info->{retries}, 1, 'job has been retried once'; ok $job->info->{delayed} > time, 'delayed timestamp'; ok $minion->job($id)->remove, 'job has been removed'; $worker->unregister; } # end SKIP my $pid; SKIP: { skip 'Minion workers do not support fork emulation', 15 if HAS_PSEUDOFORK; # Events my $failed = 0; $finished = 0; $minion->once( worker => sub { my ($minion, $worker) = @_; $worker->on( dequeue => sub { my ($worker, $job) = @_; $job->on(failed => sub { $failed++ }); $job->on(finished => sub { $finished++ }); $job->on(spawn => sub { $pid = pop }); $job->on( start => sub { my $job = shift; return unless $job->task eq 'switcheroo'; $job->task('add')->args->[-1] += 1; } ); $job->on( finish => sub { my $job = shift; return unless defined(my $old = $job->info->{notes}{finish_count}); $job->note(finish_count => $old + 1, pid => $$); } ); } ); } ); $worker = $minion->worker->register; $minion->enqueue(add => [3, 3]); $minion->enqueue(add => [4, 3]); $job = $worker->dequeue(0); is $failed, 0, 'failed event has not been emitted'; is $finished, 0, 'finished event has not been emitted'; my $result; $job->on(finished => sub { $result = pop }); ok $job->finish('Everything is fine!'), 'job finished'; $job->perform; is $result, 'Everything is fine!', 'right result'; is $failed, 0, 'failed event has not been emitted'; is $finished, 1, 'finished event has been emitted once'; isnt $pid, $$, 'new process id'; $job = $worker->dequeue(0); my $err; $job->on(failed => sub { $err = pop }); $job->fail("test\n"); $job->fail; is $err, "test\n", 'right error'; is $failed, 1, 'failed event has been emitted once'; is $finished, 1, 'finished event has been emitted once'; $minion->add_task(switcheroo => sub { }); $minion->enqueue( switcheroo => [5, 3] => {notes => {finish_count => 0, before => 23}}); $job = $worker->dequeue(0); $job->perform; is_deeply $job->info->{result}, {added => 9}, 'right result'; is $job->info->{notes}{finish_count}, 1, 'finish event has been emitted once'; ok $job->info->{notes}{pid}, 'has a process id'; isnt $job->info->{notes}{pid}, $$, 'different process id'; is $job->info->{notes}{before}, 23, 'value still exists'; $worker->unregister; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 12 if HAS_PSEUDOFORK; # Queues $id = $minion->enqueue(add => [100, 1]); is $worker->register->dequeue(0 => {queues => ['test1']}), undef, 'wrong queue'; $job = $worker->dequeue(0); is $job->id, $id, 'right id'; is $job->info->{queue}, 'default', 'right queue'; ok $job->finish, 'job finished'; $id = $minion->enqueue(add => [100, 3] => {queue => 'test1'}); is $worker->dequeue(0), undef, 'wrong queue'; $job = $worker->dequeue(0 => {queues => ['test1']}); is $job->id, $id, 'right id'; is $job->info->{queue}, 'test1', 'right queue'; ok $job->finish, 'job finished'; ok $job->retry({queue => 'test2'}), 'job retried'; $job = $worker->dequeue(0 => {queues => ['default', 'test2']}); is $job->id, $id, 'right id'; is $job->info->{queue}, 'test2', 'right queue'; ok $job->finish, 'job finished'; $worker->unregister; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 13 if HAS_PSEUDOFORK; # Failed jobs $id = $minion->enqueue(add => [5, 6]); $job = $worker->register->dequeue(0); is $job->id, $id, 'right id'; is $job->info->{result}, undef, 'no result'; ok $job->fail, 'job failed'; ok !$job->finish, 'job not finished'; is $job->info->{state}, 'failed', 'right state'; is $job->info->{result}, 'Unknown error', 'right result'; $id = $minion->enqueue(add => [6, 7]); $job = $worker->dequeue(0); is $job->id, $id, 'right id'; ok $job->fail('Something bad happened!'), 'job failed'; is $job->info->{state}, 'failed', 'right state'; is $job->info->{result}, 'Something bad happened!', 'right result'; $id = $minion->enqueue('fail'); $job = $worker->dequeue(0); is $job->id, $id, 'right id'; $job->perform; is $job->info->{state}, 'failed', 'right state'; is $job->info->{result}, "Intentional failure!\n", 'right result'; $worker->unregister; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 7 if HAS_PSEUDOFORK; # Nested data structures $minion->add_task( nested => sub { my ($job, $hash, $array) = @_; $job->note(bar => {baz => [1, 2, 3]}); $job->note(baz => 'yada'); $job->finish([{23 => $hash->{first}[0]{second} x $array->[0][0]}]); } ); $minion->enqueue( 'nested', [{first => [{second => 'test'}]}, [[3]]], {notes => {foo => [4, 5, 6]}} ); $job = $worker->register->dequeue(0); $job->perform; is $job->info->{state}, 'finished', 'right state'; ok $job->note(yada => ['works']), 'added metadata'; ok !$minion->backend->note(-1, {yada => ['failed']}), 'not added metadata'; my $notes = { foo => [4, 5, 6], bar => {baz => [1, 2, 3]}, baz => 'yada', yada => ['works'] }; is_deeply $job->info->{notes}, $notes, 'right metadata'; is_deeply $job->info->{result}, [{23 => 'testtesttest'}], 'right structure'; ok $job->note(yada => undef, bar => undef), 'removed metadata'; $notes = {foo => [4, 5, 6], baz => 'yada'}; is_deeply $job->info->{notes}, $notes, 'right metadata'; $worker->unregister; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 2 if HAS_PSEUDOFORK; # Perform job in a running event loop $id = $minion->enqueue(add => [8, 9]); Mojo::IOLoop->delay(sub { $minion->perform_jobs })->wait; is $minion->job($id)->info->{state}, 'finished', 'right state'; is_deeply $minion->job($id)->info->{result}, {added => 17}, 'right result'; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 3 if HAS_PSEUDOFORK; # Non-zero exit status $minion->add_task(exit => sub { exit 1 }); $id = $minion->enqueue('exit'); $job = $worker->register->dequeue(0); is $job->id, $id, 'right id'; $job->perform; is $job->info->{state}, 'failed', 'right state'; is $job->info->{result}, 'Non-zero exit status (1)', 'right result'; $worker->unregister; } # end SKIP # Multiple attempts while processing is $minion->backoff->(0), 15, 'right result'; is $minion->backoff->(1), 16, 'right result'; is $minion->backoff->(2), 31, 'right result'; is $minion->backoff->(3), 96, 'right result'; is $minion->backoff->(4), 271, 'right result'; is $minion->backoff->(5), 640, 'right result'; is $minion->backoff->(25), 390640, 'right result'; SKIP: { skip 'Minion workers do not support fork emulation', 16 if HAS_PSEUDOFORK; $id = $minion->enqueue(exit => [] => {attempts => 2}); $job = $worker->register->dequeue(0); is $job->id, $id, 'right id'; is $job->retries, 0, 'job has not been retried'; $job->perform; $info = $job->info; is $info->{attempts}, 2, 'job will be attempted twice'; is $info->{state}, 'inactive', 'right state'; is $info->{result}, 'Non-zero exit status (1)', 'right result'; ok $info->{retried} < $info->{delayed}, 'delayed timestamp'; $minion->backend->sqlite->db->query( q{update minion_jobs set delayed = datetime('now') where id = ?}, $id); $job = $worker->register->dequeue(0); is $job->id, $id, 'right id'; is $job->retries, 1, 'job has been retried once'; $job->perform; $info = $job->info; is $info->{attempts}, 2, 'job will be attempted twice'; is $info->{state}, 'failed', 'right state'; is $info->{result}, 'Non-zero exit status (1)', 'right result'; ok $job->retry({attempts => 3}), 'job retried'; $job = $worker->register->dequeue(0); is $job->id, $id, 'right id'; $job->perform; $info = $job->info; is $info->{attempts}, 3, 'job will be attempted three times'; is $info->{state}, 'failed', 'right state'; is $info->{result}, 'Non-zero exit status (1)', 'right result'; $worker->unregister; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 11 if HAS_PSEUDOFORK; # Multiple attempts during maintenance $id = $minion->enqueue(exit => [] => {attempts => 2}); $job = $worker->register->dequeue(0); is $job->id, $id, 'right id'; is $job->retries, 0, 'job has not been retried'; is $job->info->{attempts}, 2, 'job will be attempted twice'; is $job->info->{state}, 'active', 'right state'; $worker->unregister; $minion->repair; is $job->info->{state}, 'inactive', 'right state'; is $job->info->{result}, 'Worker went away', 'right result'; ok $job->info->{retried} < $job->info->{delayed}, 'delayed timestamp'; $minion->backend->sqlite->db->query( q{update minion_jobs set delayed = datetime('now') where id = ?}, $id); $job = $worker->register->dequeue(0); is $job->id, $id, 'right id'; is $job->retries, 1, 'job has been retried once'; $worker->unregister; $minion->repair; is $job->info->{state}, 'failed', 'right state'; is $job->info->{result}, 'Worker went away', 'right result'; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 12 if HAS_PSEUDOFORK; # A job needs to be dequeued again after a retry $minion->add_task(restart => sub { }); $id = $minion->enqueue('restart'); $job = $worker->register->dequeue(0); is $job->id, $id, 'right id'; ok $job->finish, 'job finished'; is $job->info->{state}, 'finished', 'right state'; ok $job->retry, 'job retried'; is $job->info->{state}, 'inactive', 'right state'; $job2 = $worker->dequeue(0); is $job->info->{state}, 'active', 'right state'; ok !$job->finish, 'job not finished'; is $job->info->{state}, 'active', 'right state'; is $job2->id, $id, 'right id'; ok $job2->finish, 'job finished'; ok !$job->retry, 'job not retried'; is $job->info->{state}, 'finished', 'right state'; $worker->unregister; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 8 if HAS_PSEUDOFORK; # Perform jobs concurrently $id = $minion->enqueue(add => [10, 11]); $id2 = $minion->enqueue(add => [12, 13]); $id3 = $minion->enqueue('test'); my $id4 = $minion->enqueue('exit'); $worker = $minion->worker->register; $job = $worker->dequeue(0); $job2 = $worker->dequeue(0); my $job3 = $worker->dequeue(0); my $job4 = $worker->dequeue(0); $pid = $job->start; my $pid2 = $job2->start; my $pid3 = $job3->start; my $pid4 = $job4->start; my ($first, $second, $third, $fourth); usleep 50000 until $first ||= $job->is_finished and $second ||= $job2->is_finished and $third ||= $job3->is_finished and $fourth ||= $job4->is_finished; is $minion->job($id)->info->{state}, 'finished', 'right state'; is_deeply $minion->job($id)->info->{result}, {added => 21}, 'right result'; is $minion->job($id2)->info->{state}, 'finished', 'right state'; is_deeply $minion->job($id2)->info->{result}, {added => 25}, 'right result'; is $minion->job($id3)->info->{state}, 'finished', 'right state'; is $minion->job($id3)->info->{result}, undef, 'no result'; is $minion->job($id4)->info->{state}, 'failed', 'right state'; is $minion->job($id4)->info->{result}, 'Non-zero exit status (1)', 'right result'; $worker->unregister; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 4 if HAS_PSEUDOFORK; # Stopping jobs $minion->add_task(long_running => sub { sleep 1000 }); $worker = $minion->worker->register; $minion->enqueue('long_running'); $job = $worker->dequeue(0); ok $job->start->pid, 'has a process id'; ok !$job->is_finished, 'job is not finished'; $job->stop; usleep 5000 until $job->is_finished; is $job->info->{state}, 'failed', 'right state'; like $job->info->{result}, qr/Non-zero exit status/, 'right result'; $worker->unregister; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 30 if HAS_PSEUDOFORK; # Job dependencies $worker = $minion->remove_after(0)->worker->register; is $minion->repair->stats->{finished_jobs}, 0, 'no finished jobs'; $id = $minion->enqueue('test'); $id2 = $minion->enqueue('test'); $id3 = $minion->enqueue(test => [] => {parents => [$id, $id2]}); $job = $worker->dequeue(0); is $job->id, $id, 'right id'; is_deeply $job->info->{children}, [$id3], 'right children'; is_deeply $job->info->{parents}, [], 'right parents'; $job2 = $worker->dequeue(0); is $job2->id, $id2, 'right id'; is_deeply $job2->info->{children}, [$id3], 'right children'; is_deeply $job2->info->{parents}, [], 'right parents'; ok !$worker->dequeue(0), 'parents are not ready yet'; ok $job->finish, 'job finished'; ok !$worker->dequeue(0), 'parents are not ready yet'; ok $job2->fail, 'job failed'; ok !$worker->dequeue(0), 'parents are not ready yet'; ok $job2->retry, 'job retried'; $job2 = $worker->dequeue(0); is $job2->id, $id2, 'right id'; ok $job2->finish, 'job finished'; $job = $worker->dequeue(0); is $job->id, $id3, 'right id'; is_deeply $job->info->{children}, [], 'right children'; is_deeply $job->info->{parents}, [$id, $id2], 'right parents'; is $minion->stats->{finished_jobs}, 2, 'two finished jobs'; is $minion->repair->stats->{finished_jobs}, 2, 'two finished jobs'; ok $job->finish, 'job finished'; is $minion->stats->{finished_jobs}, 3, 'three finished jobs'; is $minion->repair->stats->{finished_jobs}, 0, 'no finished jobs'; $id = $minion->enqueue(test => [] => {parents => [-1]}); $job = $worker->dequeue(0); is $job->id, $id, 'right id'; ok $job->finish, 'job finished'; $id = $minion->enqueue(test => [] => {parents => [-1]}); $job = $worker->dequeue(0); is $job->id, $id, 'right id'; is_deeply $job->info->{parents}, [-1], 'right parents'; $job->retry({parents => [-1, -2]}); $job = $worker->dequeue(0); is $job->id, $id, 'right id'; is_deeply $job->info->{parents}, [-1, -2], 'right parents'; ok $job->finish, 'job finished'; $worker->unregister; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 25 if HAS_PSEUDOFORK; # Foreground $id = $minion->enqueue(test => [] => {attempts => 2}); $id2 = $minion->enqueue('test'); $id3 = $minion->enqueue(test => [] => {parents => [$id, $id2]}); ok !$minion->foreground($id3 + 1), 'job does not exist'; ok !$minion->foreground($id3), 'job is not ready yet'; $info = $minion->job($id)->info; is $info->{attempts}, 2, 'job will be attempted twice'; is $info->{state}, 'inactive', 'right state'; is $info->{queue}, 'default', 'right queue'; ok $minion->foreground($id), 'performed first job'; $info = $minion->job($id)->info; is $info->{attempts}, 1, 'job will be attempted once'; is $info->{retries}, 1, 'job has been retried'; is $info->{state}, 'finished', 'right state'; is $info->{queue}, 'minion_foreground', 'right queue'; ok $minion->foreground($id2), 'performed second job'; $info = $minion->job($id2)->info; is $info->{retries}, 1, 'job has been retried'; is $info->{state}, 'finished', 'right state'; is $info->{queue}, 'minion_foreground', 'right queue'; ok $minion->foreground($id3), 'performed third job'; $info = $minion->job($id3)->info; is $info->{retries}, 2, 'job has been retried twice'; is $info->{state}, 'finished', 'right state'; is $info->{queue}, 'minion_foreground', 'right queue'; $id = $minion->enqueue('fail'); eval { $minion->foreground($id) }; like $@, qr/Intentional failure!/, 'right error'; $info = $minion->job($id)->info; ok $info->{worker}, 'has worker'; ok !$minion->backend->list_workers(0, 1, {ids => [$info->{worker}]}) ->{workers}[0], 'not registered'; is $info->{retries}, 1, 'job has been retried'; is $info->{state}, 'failed', 'right state'; is $info->{queue}, 'minion_foreground', 'right queue'; is $info->{result}, "Intentional failure!\n", 'right result'; } # end SKIP SKIP: { skip 'Minion workers do not support fork emulation', 9 if HAS_PSEUDOFORK; # Worker remote control commands $worker = $minion->worker->register->process_commands; $worker2 = $minion->worker->register; 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; ok $minion->backend->broadcast('test_id', [], [$worker->id]), 'sent command'; ok $minion->backend->broadcast('test_id', [], [$worker->id, $worker2->id]), 'sent command'; $worker->process_commands->register; $worker2->process_commands; is_deeply \@commands, [$worker->id, $worker->id, $worker2->id], 'right structure'; @commands = (); ok $minion->backend->broadcast('test_id'), 'sent command'; ok $minion->backend->broadcast('test_whatever'), 'sent command'; ok $minion->backend->broadcast('test_args', [23], []), 'sent command'; ok $minion->backend->broadcast('test_args', [1, [2], {3 => 'three'}], [$worker->id]), 'sent command'; $_->process_commands for $worker, $worker2; is_deeply \@commands, [$worker->id, [23], [1, [2], {3 => 'three'}], $worker2->id], 'right structure'; $_->unregister for $worker, $worker2; ok !$minion->backend->broadcast('test_id', []), 'command not sent'; } # end SKIP $minion->reset; done_testing(); CONTRIBUTING.md100644001750001750 1051613522042164 17774 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005# HOW TO CONTRIBUTE Thank you for considering contributing to this distribution. This file contains instructions that will help you work with the source code. The distribution is managed with [Dist::Zilla](https://metacpan.org/pod/Dist::Zilla). This means that many of the usual files you might expect are not in the repository, but are generated at release time. Some generated files are kept in the repository as a convenience (e.g. Build.PL/Makefile.PL and META.json). Generally, **you do not need Dist::Zilla to contribute patches**. You may need Dist::Zilla to create a tarball. See below for guidance. ## Getting dependencies If you have App::cpanminus 1.6 or later installed, you can use [cpanm](https://metacpan.org/pod/cpanm) to satisfy dependencies like this: $ cpanm --installdeps --with-develop . You can also run this command (or any other cpanm command) without installing App::cpanminus first, using the fatpacked `cpanm` script via curl or wget: $ curl -L https://cpanmin.us | perl - --installdeps --with-develop . $ wget -qO - https://cpanmin.us | perl - --installdeps --with-develop . Otherwise, look for either a `cpanfile` or `META.json` file for a list of dependencies to satisfy. ## Running tests You can run tests directly using the `prove` tool: $ prove -l $ prove -lv t/some_test_file.t For most of my distributions, `prove` is entirely sufficient for you to test any patches you have. I use `prove` for 99% of my testing during development. ## Code style and tidying Please try to match any existing coding style. If there is a `.perltidyrc` file, please install Perl::Tidy and use perltidy before submitting patches. ## Installing and using Dist::Zilla [Dist::Zilla](https://metacpan.org/pod/Dist::Zilla) is a very powerful authoring tool, optimized for maintaining a large number of distributions with a high degree of automation, but it has a large dependency chain, a bit of a learning curve and requires a number of author-specific plugins. To install it from CPAN, I recommend one of the following approaches for the quickest installation: # using CPAN.pm, but bypassing non-functional pod tests $ cpan TAP::Harness::Restricted $ PERL_MM_USE_DEFAULT=1 HARNESS_CLASS=TAP::Harness::Restricted cpan Dist::Zilla # using cpanm, bypassing *all* tests $ cpanm -n Dist::Zilla In either case, it's probably going to take about 10 minutes. Go for a walk, go get a cup of your favorite beverage, take a bathroom break, or whatever. When you get back, Dist::Zilla should be ready for you. Then you need to install any plugins specific to this distribution: $ dzil authordeps --missing | cpanm You can use Dist::Zilla to install the distribution's dependencies if you haven't already installed them with cpanm: $ dzil listdeps --missing --develop | cpanm You can instead combine these two steps into one command by installing Dist::Zilla::App::Command::installdeps then running: $ dzil installdeps Once everything is installed, here are some dzil commands you might try: $ dzil build $ dzil test $ dzil regenerate You can learn more about Dist::Zilla at http://dzil.org/ ## Other notes This distribution maintains the generated `META.json` and either `Makefile.PL` or `Build.PL` in the repository. This allows two things: [Travis CI](https://travis-ci.org/) can build and test the distribution without requiring Dist::Zilla, and the distribution can be installed directly from Github or a local git repository using `cpanm` for testing (again, not requiring Dist::Zilla). $ cpanm git://github.com/Author/Distribution-Name.git $ cd Distribution-Name; cpanm . Contributions are preferred in the form of a Github pull request. See [Using pull requests](https://help.github.com/articles/using-pull-requests/) for further information. You can use the Github issue tracker to report issues without an accompanying patch. # CREDITS This file was adapted from an initial `CONTRIBUTING.mkdn` file from David Golden under the terms of the [CC0](https://creativecommons.org/share-your-work/public-domain/cc0/), with inspiration from the contributing documents from [Dist::Zilla::Plugin::Author::KENTNL::CONTRIBUTING](https://metacpan.org/pod/Dist::Zilla::Plugin::Author::KENTNL::CONTRIBUTING) and [Dist::Zilla::PluginBundle::Author::ETHER](https://metacpan.org/pod/Dist::Zilla::PluginBundle::Author::ETHER). 00-report-prereqs.t100644001750001750 1345213522042164 21404 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005/t#!perl use strict; use warnings; # This test was generated by Dist::Zilla::Plugin::Test::ReportPrereqs 0.027 use Test::More tests => 1; use Module::Metadata; use File::Spec; # from $version::LAX my $lax_version_re = qr/(?: undef | (?: (?:[0-9]+) (?: \. | (?:\.[0-9]+) (?:_[0-9]+)? )? | (?:\.[0-9]+) (?:_[0-9]+)? ) | (?: v (?:[0-9]+) (?: (?:\.[0-9]+)+ (?:_[0-9]+)? )? | (?:[0-9]+)? (?:\.[0-9]+){2,} (?:_[0-9]+)? ) )/x; # hide optional CPAN::Meta modules from prereq scanner # and check if they are available my $cpan_meta = "CPAN::Meta"; my $cpan_meta_pre = "CPAN::Meta::Prereqs"; my $HAS_CPAN_META = eval "require $cpan_meta; $cpan_meta->VERSION('2.120900')" && eval "require $cpan_meta_pre"; ## no critic # Verify requirements? my $DO_VERIFY_PREREQS = 1; sub _max { my $max = shift; $max = ( $_ > $max ) ? $_ : $max for @_; return $max; } sub _merge_prereqs { my ($collector, $prereqs) = @_; # CPAN::Meta::Prereqs object if (ref $collector eq $cpan_meta_pre) { return $collector->with_merged_prereqs( CPAN::Meta::Prereqs->new( $prereqs ) ); } # Raw hashrefs for my $phase ( keys %$prereqs ) { for my $type ( keys %{ $prereqs->{$phase} } ) { for my $module ( keys %{ $prereqs->{$phase}{$type} } ) { $collector->{$phase}{$type}{$module} = $prereqs->{$phase}{$type}{$module}; } } } return $collector; } my @include = qw( ); my @exclude = qw( ); # Add static prereqs to the included modules list my $static_prereqs = do './t/00-report-prereqs.dd'; # Merge all prereqs (either with ::Prereqs or a hashref) my $full_prereqs = _merge_prereqs( ( $HAS_CPAN_META ? $cpan_meta_pre->new : {} ), $static_prereqs ); # Add dynamic prereqs to the included modules list (if we can) my ($source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; my $cpan_meta_error; if ( $source && $HAS_CPAN_META && (my $meta = eval { CPAN::Meta->load_file($source) } ) ) { $full_prereqs = _merge_prereqs($full_prereqs, $meta->prereqs); } else { $cpan_meta_error = $@; # capture error from CPAN::Meta->load_file($source) $source = 'static metadata'; } my @full_reports; my @dep_errors; my $req_hash = $HAS_CPAN_META ? $full_prereqs->as_string_hash : $full_prereqs; # Add static includes into a fake section for my $mod (@include) { $req_hash->{other}{modules}{$mod} = 0; } for my $phase ( qw(configure build test runtime develop other) ) { next unless $req_hash->{$phase}; next if ($phase eq 'develop' and not $ENV{AUTHOR_TESTING}); for my $type ( qw(requires recommends suggests conflicts modules) ) { next unless $req_hash->{$phase}{$type}; my $title = ucfirst($phase).' '.ucfirst($type); my @reports = [qw/Module Want Have/]; for my $mod ( sort keys %{ $req_hash->{$phase}{$type} } ) { next if $mod eq 'perl'; next if grep { $_ eq $mod } @exclude; my $file = $mod; $file =~ s{::}{/}g; $file .= ".pm"; my ($prefix) = grep { -e File::Spec->catfile($_, $file) } @INC; my $want = $req_hash->{$phase}{$type}{$mod}; $want = "undef" unless defined $want; $want = "any" if !$want && $want == 0; my $req_string = $want eq 'any' ? 'any version required' : "version '$want' required"; if ($prefix) { my $have = Module::Metadata->new_from_file( File::Spec->catfile($prefix, $file) )->version; $have = "undef" unless defined $have; push @reports, [$mod, $want, $have]; if ( $DO_VERIFY_PREREQS && $HAS_CPAN_META && $type eq 'requires' ) { if ( $have !~ /\A$lax_version_re\z/ ) { push @dep_errors, "$mod version '$have' cannot be parsed ($req_string)"; } elsif ( ! $full_prereqs->requirements_for( $phase, $type )->accepts_module( $mod => $have ) ) { push @dep_errors, "$mod version '$have' is not in required range '$want'"; } } } else { push @reports, [$mod, $want, "missing"]; if ( $DO_VERIFY_PREREQS && $type eq 'requires' ) { push @dep_errors, "$mod is not installed ($req_string)"; } } } if ( @reports ) { push @full_reports, "=== $title ===\n\n"; my $ml = _max( map { length $_->[0] } @reports ); my $wl = _max( map { length $_->[1] } @reports ); my $hl = _max( map { length $_->[2] } @reports ); if ($type eq 'modules') { splice @reports, 1, 0, ["-" x $ml, "", "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s\n", -$ml, $_->[0], $hl, $_->[2]) } @reports; } else { splice @reports, 1, 0, ["-" x $ml, "-" x $wl, "-" x $hl]; push @full_reports, map { sprintf(" %*s %*s %*s\n", -$ml, $_->[0], $wl, $_->[1], $hl, $_->[2]) } @reports; } push @full_reports, "\n"; } } } if ( @full_reports ) { diag "\nVersions for all modules listed in $source (including optional ones):\n\n", @full_reports; } if ( $cpan_meta_error || @dep_errors ) { diag "\n*** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ***\n"; } if ( $cpan_meta_error ) { my ($orig_source) = grep { -f } 'MYMETA.json', 'MYMETA.yml'; diag "\nCPAN::Meta->load_file('$orig_source') failed with: $cpan_meta_error\n"; } if ( @dep_errors ) { diag join("\n", "\nThe following REQUIRED prerequisites were not satisfied:\n", @dep_errors, "\n" ); } pass; # vim: ts=4 sts=4 sw=4 et: author000755001750001750 013522042164 17315 5ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005/xtpod-syntax.t100644001750001750 25213522042164 21727 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005/xt/author#!perl # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use strict; use warnings; use Test::More; use Test::Pod 1.41; all_pod_files_ok(); 00-report-prereqs.dd100644001750001750 320613522042164 21504 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005/tdo { my $x = { 'configure' => { 'requires' => { 'Module::Build::Tiny' => '0.034' } }, 'develop' => { 'requires' => { 'Pod::Coverage::TrustPod' => '0', 'Test::Pod' => '1.41', 'Test::Pod::Coverage' => '1.08' } }, 'runtime' => { 'requires' => { 'List::Util' => '0', 'Minion' => '9.0', 'Mojo::SQLite' => '3.000', 'Mojolicious' => '7.29', 'Sys::Hostname' => '0', 'Time::HiRes' => '0', 'perl' => '5.010001' }, 'suggests' => { 'Mojo::JSON::MaybeXS' => '0' } }, 'test' => { 'recommends' => { 'CPAN::Meta' => '2.120900' }, 'requires' => { 'File::Spec' => '0', 'Module::Metadata' => '0', 'Test::More' => '0.88' } } }; $x; }pod-coverage.t100644001750001750 33413522042164 22175 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005/xt/author#!perl # This file was automatically generated by Dist::Zilla::Plugin::PodCoverageTests. use Test::Pod::Coverage 1.08; use Pod::Coverage::TrustPod; all_pod_coverage_ok({ coverage_class => 'Pod::Coverage::TrustPod' }); examples000755001750001750 013522042164 17176 5ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005minion_bench.pl100644001750001750 655213522042164 22333 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005/examplesuse Mojo::Base -strict; use Minion; use Mojo::File 'tempdir'; use Mojo::URL; use Time::HiRes 'time'; my $ENQUEUE = 10000; my $DEQUEUE = 1000; my $REPETITIONS = 2; my $WORKERS = 4; my $INFO = 100; my $STATS = 100; my $REPAIR = 100; my $LOCK = 1000; my $UNLOCK = 1000; my $tempdir = tempdir; my $url = Mojo::URL->new->scheme('sqlite')->path($tempdir->child('temp.db')); { # XXX: Scope minion object for cleanup of tempdir # A benchmark script for comparing backends and evaluating the performance # impact of proposed changes my $minion = Minion->new(SQLite => $url); $minion->add_task(foo => sub { }); $minion->add_task(bar => sub { }); $minion->reset; # Enqueue say "Clean start with $ENQUEUE jobs"; my @parents = map { $minion->enqueue('foo') } 1 .. 5; my $before = time; $minion->enqueue($_ % 2 ? 'foo' : 'bar' => [] => {parents => \@parents}) for 1 .. $ENQUEUE; my $elapsed = time - $before; my $avg = sprintf '%.3f', $ENQUEUE / $elapsed; say "Enqueued $ENQUEUE jobs in $elapsed seconds ($avg/s)"; #$minion->backend->sqlite->db->query('analyze minion_jobs'); # XXX: disconnect open database handle before forking to prevent database corruption $minion = Minion->new(SQLite => $url); $minion->add_task(foo => sub { }); $minion->add_task(bar => sub { }); # Dequeue sub dequeue { my @pids; for (1 .. $WORKERS) { 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; my $elapsed = time - $before; my $avg = sprintf '%.3f', $DEQUEUE / $elapsed; say "$$ finished $DEQUEUE jobs in $elapsed seconds ($avg/s)"; $worker->unregister; exit; } push @pids, $pid; } say "$$ has started $WORKERS workers"; 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)"; } dequeue() for 1 .. $REPETITIONS; # Job info say "Requesting job info $INFO times"; $before = time; my $backend = $minion->backend; $backend->list_jobs(0, 1, {ids => [$_]}) for 1 .. $INFO; $elapsed = time - $before; $avg = sprintf '%.3f', $INFO / $elapsed; say "Received job info $INFO times in $elapsed seconds ($avg/s)"; # Stats say "Requesting stats $STATS times"; $before = time; $minion->stats for 1 .. $STATS; $elapsed = time - $before; $avg = sprintf '%.3f', $STATS / $elapsed; say "Received stats $STATS times in $elapsed seconds ($avg/s)"; # Repair say "Repairing $REPAIR times"; $before = time; $minion->repair for 1 .. $REPAIR; $elapsed = time - $before; $avg = sprintf '%.3f', $REPAIR / $elapsed; say "Repaired $REPAIR times in $elapsed seconds ($avg/s)"; # Lock say "Acquiring locks $LOCK times"; $before = time; $minion->lock($_ % 2 ? 'foo' : 'bar', 3600, {limit => int($LOCK / 2)}) for 1 .. $LOCK; $elapsed = time - $before; $avg = sprintf '%.3f', $LOCK / $elapsed; say "Acquired locks $LOCK times in $elapsed seconds ($avg/s)"; # Unlock say "Releasing locks $UNLOCK times"; $before = time; $minion->unlock($_ % 2 ? 'foo' : 'bar') for 1 .. $UNLOCK; $elapsed = time - $before; $avg = sprintf '%.3f', $UNLOCK / $elapsed; say "Releasing locks $UNLOCK times in $elapsed seconds ($avg/s)"; } sqlite_admin.t100644001750001750 1341313522042164 22335 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005/xt/authoruse Mojo::Base -strict; BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } use Test::More; use Mojolicious::Lite; use Mojo::SQLite; use Test::Mojo; my $sql = Mojo::SQLite->new; plugin Minion => {SQLite => $sql}; app->minion->add_task(test => sub { }); my $finished = app->minion->enqueue('test'); app->minion->perform_jobs; my $inactive = app->minion->enqueue('test'); get '/home' => 'test_home'; plugin 'Minion::Admin'; my $t = Test::Mojo->new; # Dashboard $t->get_ok('/minion')->status_is(200)->content_like(qr/Dashboard/) ->element_exists('a[href=/]'); # Stats $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'); # Jobs $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/) ->text_unlike('tbody td a' => qr/$inactive/); # Workers $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') ->text_like('tbody td a' => qr/@{[$worker->id]}/); $worker->unregister; $t->get_ok('/minion/workers')->status_is(200)->element_exists_not('tbody td a'); # Locks $t->app->minion->lock('foo', 3600); $t->app->minion->lock('bar', 3600); $t->ua->max_redirects(5); $t->get_ok('/minion/locks')->status_is(200)->text_like('tbody td a' => qr/bar/); $t->get_ok('/minion/locks?name=foo')->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') ->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'; # Manage jobs is app->minion->job($finished)->info->{state}, 'finished', 'right state'; $t->post_ok( '/minion/jobs?_method=PATCH' => form => {id => $finished, do => 'retry'}) ->text_like('.alert-success', qr/All selected jobs retried/); is $t->tx->previous->res->code, 302, 'right status'; like $t->tx->previous->res->headers->location, qr/id=$finished/, 'right "Location" value'; is app->minion->job($finished)->info->{state}, 'inactive', 'right state'; $t->post_ok( '/minion/jobs?_method=PATCH' => form => {id => $finished, do => 'stop'}) ->text_like('.alert-info', qr/Trying to stop all selected jobs/); is $t->tx->previous->res->code, 302, 'right status'; like $t->tx->previous->res->headers->location, qr/id=$finished/, 'right "Location" value'; $t->post_ok( '/minion/jobs?_method=PATCH' => form => {id => $finished, do => 'remove'}) ->text_like('.alert-success', qr/All selected jobs removed/); is $t->tx->previous->res->code, 302, 'right status'; like $t->tx->previous->res->headers->location, qr/id=$finished/, 'right "Location" value'; is app->minion->job($finished), undef, 'job has been removed'; # Bundled static files $t->get_ok('/minion/bootstrap/bootstrap.js')->status_is(200) ->content_type_is('application/javascript'); $t->get_ok('/minion/bootstrap/bootstrap.css')->status_is(200) ->content_type_is('text/css'); $t->get_ok('/minion/d3/d3.js')->status_is(200) ->content_type_is('application/javascript'); $t->get_ok('/minion/epoch/epoch.js')->status_is(200) ->content_type_is('application/javascript'); $t->get_ok('/minion/epoch/epoch.css')->status_is(200) ->content_type_is('text/css'); $t->get_ok('/minion/fontawesome/fontawesome.css')->status_is(200) ->content_type_is('text/css'); $t->get_ok('/minion/popper/popper.js')->status_is(200) ->content_type_is('application/javascript'); $t->get_ok('/minion/webfonts/fa-brands-400.eot')->status_is(200); $t->get_ok('/minion/webfonts/fa-brands-400.ttf')->status_is(200); $t->get_ok('/minion/webfonts/fa-brands-400.woff')->status_is(200); $t->get_ok('/minion/webfonts/fa-brands-400.woff2')->status_is(200); $t->get_ok('/minion/webfonts/fa-regular-400.eot')->status_is(200); $t->get_ok('/minion/webfonts/fa-regular-400.ttf')->status_is(200); $t->get_ok('/minion/webfonts/fa-regular-400.woff')->status_is(200); $t->get_ok('/minion/webfonts/fa-regular-400.woff2')->status_is(200); $t->get_ok('/minion/webfonts/fa-solid-900.eot')->status_is(200); $t->get_ok('/minion/webfonts/fa-solid-900.ttf')->status_is(200); $t->get_ok('/minion/webfonts/fa-solid-900.woff')->status_is(200); $t->get_ok('/minion/webfonts/fa-solid-900.woff2')->status_is(200); $t->get_ok('/minion/moment/moment.js')->status_is(200) ->content_type_is('application/javascript'); $t->get_ok('/minion/app.js')->status_is(200) ->content_type_is('application/javascript'); $t->get_ok('/minion/app.css')->status_is(200)->content_type_is('text/css'); $t->get_ok('/minion/logo-black-2x.png')->status_is(200) ->content_type_is('image/png'); $t->get_ok('/minion/logo-black.png')->status_is(200) ->content_type_is('image/png'); # Different prefix and return route plugin 'Minion::Admin' => {route => app->routes->any('/also_minion'), return_to => 'test_home'}; $t->get_ok('/also_minion')->status_is(200)->content_like(qr/Dashboard/) ->element_exists('a[href=/home]'); done_testing(); sqlite_lite_app.t100644001750001750 276613522042164 23033 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005/xt/authoruse Mojo::Base -strict; BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } use Test::More; use Mojo::IOLoop; use Mojo::SQLite; use Mojolicious::Lite; use Test::Mojo; my $sql = Mojo::SQLite->new; plugin Minion => {SQLite => $sql}; app->minion->add_task( add => sub { my ($job, $first, $second) = @_; Mojo::IOLoop->next_tick(sub { $job->finish($first + $second); Mojo::IOLoop->stop; }); Mojo::IOLoop->start; } ); get '/add' => sub { my $c = shift; my $id = $c->minion->enqueue(add => [$c->param('first'), $c->param('second')] => { queue => 'test'}); $c->render(text => $id); }; get '/result' => sub { my $c = shift; $c->render(text => $c->minion->job($c->param('id'))->info->{result}); }; my $t = Test::Mojo->new; # Perform jobs automatically $t->get_ok('/add' => form => {first => 1, second => 2})->status_is(200); $t->app->minion->perform_jobs({queues => ['test']}); $t->get_ok('/result' => form => {id => $t->tx->res->text})->status_is(200) ->content_is('3'); $t->get_ok('/add' => form => {first => 2, second => 3})->status_is(200); my $first = $t->tx->res->text; $t->get_ok('/add' => form => {first => 4, second => 5})->status_is(200); my $second = $t->tx->res->text; Mojo::IOLoop->delay(sub { $t->app->minion->perform_jobs({queues => ['test']}) })->wait; $t->get_ok('/result' => form => {id => $first})->status_is(200) ->content_is('5'); $t->get_ok('/result' => form => {id => $second})->status_is(200) ->content_is('9'); $t->app->minion->reset; done_testing(); Backend000755001750001750 013522042164 20706 5ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005/lib/MinionSQLite.pm100644001750001750 7422613522042164 22600 0ustar00grinnzgrinnz000000000000Minion-Backend-SQLite-4.005/lib/Minion/Backendpackage Minion::Backend::SQLite; use Mojo::Base 'Minion::Backend'; use Carp 'croak'; use List::Util 'min'; use Mojo::SQLite; use Mojo::Util 'steady_time'; use Sys::Hostname 'hostname'; use Time::HiRes 'usleep'; our $VERSION = '4.005'; has dequeue_interval => 0.5; has 'sqlite'; sub new { my $self = shift->SUPER::new(sqlite => Mojo::SQLite->new(@_)); $self->sqlite->auto_migrate(1)->migrations->name('minion')->from_data; return $self; } sub broadcast { my ($self, $command, $args, $ids) = (shift, shift, shift || [], shift || []); my $ids_in = join ',', ('?')x@$ids; return !!$self->sqlite->db->query( q{update minion_workers set inbox = json_set(inbox, '$[' || json_array_length(inbox) || ']', json(?))} . (@$ids ? " where id in ($ids_in)" : ''), {json => [$command, @$args]}, @$ids )->rows; } sub dequeue { my ($self, $id, $wait, $options) = @_; my $job = $self->_try($id, $options); unless ($job) { my $int = $self->dequeue_interval; my $end = steady_time + $wait; my $remaining = $wait; usleep(min($int, $remaining) * 1000000) until ($remaining = $end - steady_time) <= 0 or $job = $self->_try($id, $options); } return $job || $self->_try($id, $options); } sub enqueue { my ($self, $task, $args, $options) = (shift, shift, shift || [], shift || {}); my $db = $self->sqlite->db; return $db->query( q{insert into minion_jobs (args, attempts, delayed, notes, parents, priority, queue, task) values (?, ?, (datetime('now', ? || ' seconds')), ?, ?, ?, ?, ?)}, {json => $args}, $options->{attempts} // 1, $options->{delay} // 0, {json => $options->{notes} || {}}, {json => ($options->{parents} || [])}, $options->{priority} // 0, $options->{queue} // 'default', $task )->last_insert_id; } sub fail_job { shift->_update(1, @_) } sub finish_job { shift->_update(0, @_) } sub history { my $self = shift; my $db = $self->sqlite->db; my $steps = $db->query( q{with recursive generate_series(ts) as ( select datetime('now','-23 hours') union all select datetime(ts,'+1 hour') from generate_series where datetime(ts,'+1 hour') <= datetime('now') ) select ts, strftime('%s',ts) as epoch, strftime('%d',ts,'localtime') as day, strftime('%H',ts,'localtime') as hour from generate_series order by epoch})->hashes; my $counts = $db->query( q{select strftime('%d',finished,'localtime') as day, strftime('%H',finished,'localtime') as hour, count(case state when 'failed' then 1 end) as failed_jobs, count(case state when 'finished' then 1 end) as finished_jobs from minion_jobs where finished > ? group by day, hour}, $steps->first->{ts})->hashes; my %daily = map { ("$_->{day}-$_->{hour}" => $_) } @$counts; my @daily_ordered; foreach my $step (@$steps) { my $hour_counts = $daily{"$step->{day}-$step->{hour}"} // {}; push @daily_ordered, { epoch => $step->{epoch}, failed_jobs => $hour_counts->{failed_jobs} // 0, finished_jobs => $hour_counts->{finished_jobs} // 0, }; } return {daily => \@daily_ordered}; } sub list_jobs { my ($self, $offset, $limit, $options) = @_; my (@where, @where_params); if (defined(my $ids = $options->{ids})) { my $ids_in = join ',', ('?')x@$ids; push @where, @$ids ? "id in ($ids_in)" : 'id is null'; push @where_params, @$ids; } if (defined(my $notes = $options->{notes})) { croak 'Listing jobs by existence of notes is unimplemented'; } if (defined(my $queues = $options->{queues})) { my $queues_in = join ',', ('?')x@$queues; push @where, @$queues ? "queue in ($queues_in)" : 'queue is null'; push @where_params, @$queues; } if (defined(my $states = $options->{states})) { my $states_in = join ',', ('?')x@$states; push @where, @$states ? "state in ($states_in)" : 'state is null'; push @where_params, @$states; } if (defined(my $tasks = $options->{tasks})) { my $tasks_in = join ',', ('?')x@$tasks; push @where, @$tasks ? "task in ($tasks_in)" : 'task is null'; push @where_params, @$tasks; } my $where_str = @where ? 'where ' . join(' and ', @where) : ''; my $jobs = $self->sqlite->db->query( qq{select id, args, attempts, (select json_group_array(distinct child.id) from minion_jobs as child, json_each(child.parents) as parent_id where j.id = parent_id.value) as children, strftime('%s',created) as created, strftime('%s',delayed) as delayed, strftime('%s',finished) as finished, notes, parents, priority, queue, result, strftime('%s',retried) as retried, retries, strftime('%s',started) as started, state, task, strftime('%s','now') as time, worker from minion_jobs as j $where_str order by id desc limit ? offset ?}, @where_params, $limit, $offset )->expand(json => [qw(args children notes parents result)])->hashes->to_array; my $total = $self->sqlite->db->query(qq{select count(*) from minion_jobs as j $where_str}, @where_params)->arrays->first->[0]; return {jobs => $jobs, total => $total}; } sub list_locks { my ($self, $offset, $limit, $options) = @_; my (@where, @where_params); push @where, q{expires > datetime('now')}; if (defined(my $names = $options->{names})) { my $names_in = join ',', ('?')x@$names; push @where, @$names ? "name in ($names_in)" : 'name is null'; push @where_params, @$names; } my $where_str = 'where ' . join(' and ', @where); my $locks = $self->sqlite->db->query( qq{select name, strftime('%s',expires) as expires from minion_locks $where_str order by id desc limit ? offset ?}, @where_params, $limit, $offset )->hashes->to_array; my $total = $self->sqlite->db->query(qq{select count(*) from minion_locks $where_str}, @where_params)->arrays->first->[0]; return {locks => $locks, total => $total}; } sub list_workers { my ($self, $offset, $limit, $options) = @_; my (@where, @where_params); if (defined(my $ids = $options->{ids})) { my $ids_in = join ',', ('?')x@$ids; push @where, @$ids ? "w.id in ($ids_in)" : 'w.id is null'; push @where_params, @$ids; } my $where_str = @where ? 'where ' . join(' and ', @where) : ''; my $workers = $self->sqlite->db->query( qq{select w.id, strftime('%s',w.notified) as notified, group_concat(j.id) as jobs, w.host, w.pid, w.status, strftime('%s',w.started) as started from minion_workers as w left join minion_jobs as j on j.worker = w.id and j.state = 'active' $where_str group by w.id order by w.id desc limit ? offset ?}, @where_params, $limit, $offset )->expand(json => 'status')->hashes->to_array; $_->{jobs} = [split /,/, ($_->{jobs} // '')] for @$workers; my $total = $self->sqlite->db->query(qq{select count(*) from minion_workers as w $where_str}, @where_params)->arrays->first->[0]; return {total => $total, workers => $workers}; } sub lock { my ($self, $name, $duration, $options) = (shift, shift, shift, shift // {}); my $db = $self->sqlite->db; $db->query(q{delete from minion_locks where expires < datetime('now')}); my $tx = $db->begin('exclusive'); my $locks = $db->query(q{select count(*) from minion_locks where name = ?}, $name)->arrays->first->[0]; return !!0 if defined $locks and $locks >= ($options->{limit} || 1); if (defined $duration and $duration > 0) { $db->query(q{insert into minion_locks (name, expires) values (?, datetime('now', ? || ' seconds'))}, $name, $duration); $tx->commit; } return !!1; } sub note { my ($self, $id, $merge) = @_; my (@set, @set_params, @remove, @remove_params); foreach my $key (keys %$merge) { croak qq{Invalid note key '$key'; must not contain '.', '[', or ']'} if $key =~ m/[\[\].]/; if (defined $merge->{$key}) { push @set, q{'$.' || ?}, 'json(?)'; push @set_params, $key, {json => $merge->{$key}}; } else { push @remove, q{'$.' || ?}; push @remove_params, $key; } } my $json_set = join ', ', @set; my $json_remove = join ', ', @remove; my $set_to = 'notes'; $set_to = "json_set($set_to, $json_set)" if @set; $set_to = "json_remove($set_to, $json_remove)" if @remove; return !!$self->sqlite->db->query( qq{update minion_jobs set notes = $set_to where id = ?}, @set_params, @remove_params, $id )->rows; } sub receive { my ($self, $id) = @_; my $db = $self->sqlite->db; my $tx = $db->begin; my $array = $db->query(q{select inbox from minion_workers where id = ?}, $id) ->expand(json => 'inbox')->array; $db->query(q{update minion_workers set inbox = '[]' where id = ?}, $id) if $array; $tx->commit; return $array ? $array->[0] : []; } sub register_worker { my ($self, $id, $options) = (shift, shift, shift || {}); return $id if $id && $self->sqlite->db->query( q{update minion_workers set notified = datetime('now'), status = ? where id = ?}, {json => $options->{status} // {}}, $id)->rows; return $self->sqlite->db->query( q{insert into minion_workers (host, pid, status) values (?, ?, ?)}, hostname, $$, {json => $options->{status} // {}})->last_insert_id; } sub remove_job { !!shift->sqlite->db->query( q{delete from minion_jobs where id = ? and state in ('inactive', 'failed', 'finished')}, shift )->rows; } sub repair { my $self = shift; # Workers without heartbeat my $db = $self->sqlite->db; my $minion = $self->minion; $db->query( q{delete from minion_workers where notified < datetime('now', '-' || ? || ' seconds')}, $minion->missing_after ); # Jobs with missing worker (can be retried) my $fail = $db->query( q{select id, retries from minion_jobs as j where state = 'active' and queue != 'minion_foreground' and not exists (select 1 from minion_workers where id = j.worker)} )->hashes; $fail->each(sub { $self->fail_job(@$_{qw(id retries)}, 'Worker went away') }); # Old jobs with no unresolved dependencies $db->query( q{delete from minion_jobs where finished <= datetime('now', '-' || ? || ' seconds') and state = 'finished' and id not in (select distinct parent_id.value from minion_jobs as child, json_each(child.parents) as parent_id where child.state <> 'finished')}, $minion->remove_after); } sub reset { my $db = shift->sqlite->db; my $tx = $db->begin; $db->query('delete from minion_jobs'); $db->query('delete from minion_locks'); $db->query('delete from minion_workers'); $db->query(q{delete from sqlite_sequence where name in ('minion_jobs','minion_locks','minion_workers')}); $tx->commit; } sub retry_job { my ($self, $id, $retries, $options) = (shift, shift, shift, shift || {}); my $parents = defined $options->{parents} ? {json => $options->{parents}} : undef; return !!$self->sqlite->db->query( q{update minion_jobs set attempts = coalesce(?, attempts), delayed = (datetime('now', ? || ' seconds')), parents = coalesce(?, parents), priority = coalesce(?, priority), queue = coalesce(?, queue), retried = datetime('now'), retries = retries + 1, state = 'inactive' where id = ? and retries = ?}, $options->{attempts}, $options->{delay} // 0, $parents, @$options{qw(priority queue)}, $id, $retries )->rows; } sub stats { my $self = shift; my $stats = $self->sqlite->db->query( q{select count(case state when 'inactive' then 1 end) as inactive_jobs, count(case state when 'active' then 1 end) as active_jobs, count(case state when 'failed' then 1 end) as failed_jobs, count(case state when 'finished' then 1 end) as finished_jobs, count(case when state = 'inactive' and delayed > datetime('now') then 1 end) as delayed_jobs, (select count(*) from minion_locks where expires > datetime('now')) as active_locks, count(distinct case when state = 'active' then worker end) as active_workers, ifnull((select seq from sqlite_sequence where name = 'minion_jobs'), 0) as enqueued_jobs, (select count(*) from minion_workers) as inactive_workers, null as uptime from minion_jobs} )->hash; $stats->{inactive_workers} -= $stats->{active_workers}; return $stats; } sub unlock { !!shift->sqlite->db->query( q{delete from minion_locks where id = ( select id from minion_locks where expires > datetime('now') and name = ? order by expires limit 1)}, shift )->rows; } sub unregister_worker { shift->sqlite->db->query('delete from minion_workers where id = ?', shift); } sub _try { my ($self, $id, $options) = @_; my $db = $self->sqlite->db; my $queues = $options->{queues} || ['default']; my $tasks = [keys %{$self->minion->tasks}]; return undef unless @$queues and @$tasks; my $queues_in = join ',', ('?')x@$queues; my $tasks_in = join ',', ('?')x@$tasks; my $tx = $db->begin; my $res = $db->query( qq{select id from minion_jobs as j where delayed <= datetime('now') and id = coalesce(?, id) and (json_array_length(parents) = 0 or not exists ( select 1 from minion_jobs as parent, json_each(j.parents) as parent_id where parent.id = parent_id.value and parent.state in ('inactive', 'active', 'failed') )) and queue in ($queues_in) and state = 'inactive' and task in ($tasks_in) order by priority desc, id limit 1}, $options->{id}, @$queues, @$tasks ); my $job_id = ($res->arrays->first // [])->[0] // return undef; $db->query( q{update minion_jobs set started = datetime('now'), state = 'active', worker = ? where id = ?}, $id, $job_id ); $tx->commit; my $info = $db->query( 'select id, args, retries, task from minion_jobs where id = ?', $job_id )->expand(json => 'args')->hash // return undef; return $info; } sub _update { my ($self, $fail, $id, $retries, $result) = @_; my $db = $self->sqlite->db; return undef unless $db->query( q{update minion_jobs set finished = datetime('now'), result = ?, state = ? where id = ? and retries = ? and state = 'active'}, {json => $result}, $fail ? 'failed' : 'finished', $id, $retries )->rows > 0; my $row = $db->query('select attempts from minion_jobs where id = ?', $id)->array; return 1 if !$fail || (my $attempts = $row->[0]) == 1; return 1 if $retries >= ($attempts - 1); my $delay = $self->minion->backoff->($retries); return $self->retry_job($id, $retries, {delay => $delay}); } 1; =encoding utf8 =head1 NAME Minion::Backend::SQLite - SQLite backend for Minion job queue =head1 SYNOPSIS use Minion::Backend::SQLite; my $backend = Minion::Backend::SQLite->new('sqlite:test.db'); # Minion use Minion; my $minion = Minion->new(SQLite => 'sqlite:test.db'); # Mojolicious (via Mojolicious::Plugin::Minion) $self->plugin(Minion => { SQLite => 'sqlite:test.db' }); # Mojolicious::Lite (via Mojolicious::Plugin::Minion) plugin Minion => { SQLite => 'sqlite:test.db' }; # Share the database connection cache helper sqlite => sub { state $sqlite = Mojo::SQLite->new('sqlite:test.db') }; plugin Minion => { SQLite => app->sqlite }; =head1 DESCRIPTION L is a backend for L based on L. All necessary tables will be created automatically with a set of migrations named C. If no connection string or C<:temp:> is provided, the database will be created in a temporary directory. =head1 ATTRIBUTES L inherits all attributes from L and implements the following new ones. =head2 dequeue_interval my $seconds = $backend->dequeue_interval; $backend = $backend->dequeue_interval($seconds); Interval in seconds between L attempts. Defaults to C<0.5>. =head2 sqlite my $sqlite = $backend->sqlite; $backend = $backend->sqlite(Mojo::SQLite->new); L object used to store all data. =head1 METHODS L inherits all methods from L and implements the following new ones. =head2 new my $backend = Minion::Backend::SQLite->new; my $backend = Minion::Backend::SQLite->new(':temp:'); my $backend = Minion::Backend::SQLite->new('sqlite:test.db'); my $backend = Minion::Backend::SQLite->new->tap(sub { $_->sqlite->from_filename('C:\\foo\\bar.db') }); my $backend = Minion::Backend::SQLite->new(Mojo::SQLite->new); Construct a new L object. =head2 broadcast my $bool = $backend->broadcast('some_command'); my $bool = $backend->broadcast('some_command', [@args]); my $bool = $backend->broadcast('some_command', [@args], [$id1, $id2, $id3]); Broadcast remote control command to one or more workers. =head2 dequeue my $job_info = $backend->dequeue($worker_id, 0.5); my $job_info = $backend->dequeue($worker_id, 0.5, {queues => ['important']}); Wait a given amount of time in seconds for a job, dequeue it and transition from C to C state, or return C if queues were empty. Jobs will be checked for in intervals defined by L until the timeout is reached. These options are currently available: =over 2 =item id id => '10023' Dequeue a specific job. =item queues queues => ['important'] One or more queues to dequeue jobs from, defaults to C. =back These fields are currently available: =over 2 =item args args => ['foo', 'bar'] Job arguments. =item id id => '10023' Job ID. =item retries retries => 3 Number of times job has been retried. =item task task => 'foo' Task name. =back =head2 enqueue my $job_id = $backend->enqueue('foo'); my $job_id = $backend->enqueue(foo => [@args]); my $job_id = $backend->enqueue(foo => [@args] => {priority => 1}); Enqueue a new job with C state. These options are currently available: =over 2 =item attempts attempts => 25 Number of times performing this job will be attempted, with a delay based on L after the first attempt, defaults to C<1>. =item delay delay => 10 Delay job for this many seconds (from now). =item notes notes => {foo => 'bar', baz => [1, 2, 3]} Hash reference with arbitrary metadata for this job. =item parents parents => [$id1, $id2, $id3] One or more existing jobs this job depends on, and that need to have transitioned to the state C before it can be processed. =item priority priority => 5 Job priority, defaults to C<0>. Jobs with a higher priority get performed first. =item queue queue => 'important' Queue to put job in, defaults to C. =back =head2 fail_job my $bool = $backend->fail_job($job_id, $retries); my $bool = $backend->fail_job($job_id, $retries, 'Something went wrong!'); my $bool = $backend->fail_job( $job_id, $retries, {msg => 'Something went wrong!'}); Transition from C to C state with or without a result, and if there are attempts remaining, transition back to C with an exponentially increasing delay based on L. =head2 finish_job my $bool = $backend->finish_job($job_id, $retries); my $bool = $backend->finish_job($job_id, $retries, 'All went well!'); my $bool = $backend->finish_job($job_id, $retries, {msg => 'All went well!'}); Transition from C to C state with or without a result. =head2 history my $history = $backend->history; Get history information for job queue. Note that this method is EXPERIMENTAL and might change without warning! These fields are currently available: =over 2 =item daily daily => [{epoch => 12345, finished_jobs => 95, failed_jobs => 2}, ...] Hourly counts for processed jobs from the past day. =back =head2 list_jobs my $results = $backend->list_jobs($offset, $limit); my $results = $backend->list_jobs($offset, $limit, {states => ['inactive']}); Returns the information about jobs in batches. # Get the total number of results (without limit) my $num = $backend->list_jobs(0, 100, {queues => ['important']})->{total}; # Check job state my $results = $backend->list_jobs(0, 1, {ids => [$job_id]}); my $state = $results->{jobs}[0]{state}; # Get job result my $results = $backend->list_jobs(0, 1, {ids => [$job_id]}); my $result = $results->{jobs}[0]{result}; These options are currently available: =over 2 =item ids ids => ['23', '24'] List only jobs with these ids. =item queues queues => ['important', 'unimportant'] List only jobs in these queues. =item states states => ['inactive', 'active'] List only jobs in these states. =item tasks tasks => ['foo', 'bar'] List only jobs for these tasks. =back These fields are currently available: =over 2 =item args args => ['foo', 'bar'] Job arguments. =item attempts attempts => 25 Number of times performing this job will be attempted. =item children children => ['10026', '10027', '10028'] Jobs depending on this job. =item created created => 784111777 Epoch time job was created. =item delayed delayed => 784111777 Epoch time job was delayed to. =item finished finished => 784111777 Epoch time job was finished. =item id id => 10025 Job id. =item notes notes => {foo => 'bar', baz => [1, 2, 3]} Hash reference with arbitrary metadata for this job. =item parents parents => ['10023', '10024', '10025'] Jobs this job depends on. =item priority priority => 3 Job priority. =item queue queue => 'important' Queue name. =item result result => 'All went well!' Job result. =item retried retried => 784111777 Epoch time job has been retried. =item retries retries => 3 Number of times job has been retried. =item started started => 784111777 Epoch time job was started. =item state state => 'inactive' Current job state, usually C, C, C or C. =item task task => 'foo' Task name. =item time time => 78411177 Current time. =item worker worker => '154' Id of worker that is processing the job. =back =head2 list_locks my $results = $backend->list_locks($offset, $limit); my $results = $backend->list_locks($offset, $limit, {names => ['foo']}); Returns information about locks in batches. # Get the total number of results (without limit) my $num = $backend->list_locks(0, 100, {names => ['bar']})->{total}; # Check expiration time my $results = $backend->list_locks(0, 1, {names => ['foo']}); my $expires = $results->{locks}[0]{expires}; These options are currently available: =over 2 =item names names => ['foo', 'bar'] List only locks with these names. =back These fields are currently available: =over 2 =item expires expires => 784111777 Epoch time this lock will expire. =item name name => 'foo' Lock name. =back =head2 list_workers my $results = $backend->list_workers($offset, $limit); my $results = $backend->list_workers($offset, $limit, {ids => [23]}); Returns information about workers in batches. # Get the total number of results (without limit) my $num = $backend->list_workers(0, 100)->{total}; # Check worker host my $results = $backend->list_workers(0, 1, {ids => [$worker_id]}); my $host = $results->{workers}[0]{host}; These options are currently available: =over 2 =item ids ids => ['23', '24'] List only workers with these ids. =back These fields are currently available: =over 2 =item id id => 22 Worker id. =item host host => 'localhost' Worker host. =item jobs jobs => ['10023', '10024', '10025', '10029'] Ids of jobs the worker is currently processing. =item notified notified => 784111777 Epoch time worker sent the last heartbeat. =item pid pid => 12345 Process id of worker. =item started started => 784111777 Epoch time worker was started. =item status status => {queues => ['default', 'important']} Hash reference with whatever status information the worker would like to share. =back =head2 lock my $bool = $backend->lock('foo', 3600); my $bool = $backend->lock('foo', 3600, {limit => 20}); Try to acquire a named lock that will expire automatically after the given amount of time in seconds. An expiration time of C<0> can be used to check if a named lock already exists without creating one. These options are currently available: =over 2 =item limit limit => 20 Number of shared locks with the same name that can be active at the same time, defaults to C<1>. =back =head2 note my $bool = $backend->note($job_id, {mojo => 'rocks', minion => 'too'}); Change one or more metadata fields for a job. It is currently an error to attempt to set a metadata field with a name containing the characters C<.>, C<[>, or C<]>. =head2 receive my $commands = $backend->receive($worker_id); Receive remote control commands for worker. =head2 register_worker my $worker_id = $backend->register_worker; my $worker_id = $backend->register_worker($worker_id); my $worker_id = $backend->register_worker( $worker_id, {status => {queues => ['default', 'important']}}); Register worker or send heartbeat to show that this worker is still alive. These options are currently available: =over 2 =item status status => {queues => ['default', 'important']} Hash reference with whatever status information the worker would like to share. =back =head2 remove_job my $bool = $backend->remove_job($job_id); Remove C, C or C job from queue. =head2 repair $backend->repair; Repair worker registry and job queue if necessary. =head2 reset $backend->reset; Reset job queue. =head2 retry_job my $bool = $backend->retry_job($job_id, $retries); my $bool = $backend->retry_job($job_id, $retries, {delay => 10}); Transition job back to C state, already C jobs may also be retried to change options. These options are currently available: =over 2 =item attempts attempts => 25 Number of times performing this job will be attempted. =item delay delay => 10 Delay job for this many seconds (from now). =item parents parents => [$id1, $id2, $id3] Jobs this job depends on. =item priority priority => 5 Job priority. =item queue queue => 'important' Queue to put job in. =back =head2 stats my $stats = $backend->stats; Get statistics for the job queue. These fields are currently available: =over 2 =item active_jobs active_jobs => 100 Number of jobs in C state. =item active_locks active_locks => 100 Number of active named locks. =item active_workers active_workers => 100 Number of workers that are currently processing a job. =item delayed_jobs delayed_jobs => 100 Number of jobs in C state that are scheduled to run at specific time in the future. Note that this field is EXPERIMENTAL and might change without warning! =item enqueued_jobs enqueued_jobs => 100000 Rough estimate of how many jobs have ever been enqueued. Note that this field is EXPERIMENTAL and might change without warning! =item failed_jobs failed_jobs => 100 Number of jobs in C state. =item finished_jobs finished_jobs => 100 Number of jobs in C state. =item inactive_jobs inactive_jobs => 100 Number of jobs in C state. =item inactive_workers inactive_workers => 100 Number of workers that are currently not processing a job. =item uptime uptime => undef Uptime in seconds. Always undefined for SQLite. =back =head2 unlock my $bool = $backend->unlock('foo'); Release a named lock. =head2 unregister_worker $backend->unregister_worker($worker_id); Unregister worker. =head1 BUGS Report any issues on the public bugtracker. =head1 AUTHOR Dan Book =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2015 by Dan Book. This is free software, licensed under: The Artistic License 2.0 (GPL Compatible) =head1 SEE ALSO L, L =cut __DATA__ @@ minion -- 1 up create table if not exists minion_jobs ( id integer not null primary key autoincrement, args blob not null, created text not null default current_timestamp, delayed text not null, finished text, priority integer not null, result blob, retried text, retries integer not null default 0, started text, state text not null default 'inactive', task text not null, worker integer, queue text not null default 'default' ); create index if not exists minion_jobs_priority_created on minion_jobs (priority desc, created); create index if not exists minion_jobs_state on minion_jobs (state); create table if not exists minion_workers ( id integer not null primary key autoincrement, host text not null, pid integer not null, started text not null default current_timestamp, notified text not null default current_timestamp ); -- 1 down drop table if exists minion_jobs; drop table if exists minion_workers; -- 2 up alter table minion_jobs add column attempts integer not null default 1; -- 3 up create table minion_jobs_NEW ( id integer not null primary key autoincrement, args text not null, created text not null default current_timestamp, delayed text not null, finished text, priority integer not null, result text, retried text, retries integer not null default 0, started text, state text not null default 'inactive', task text not null, worker integer, queue text not null default 'default', attempts integer not null default 1 ); insert into minion_jobs_NEW select * from minion_jobs; drop table minion_jobs; alter table minion_jobs_NEW rename to minion_jobs; -- 4 up alter table minion_jobs add column parents text not null default '[]'; -- 5 up alter table minion_workers add column inbox text not null check(json_valid(inbox) and json_type(inbox) = 'array') default '[]'; -- 6 up drop index if exists minion_jobs_priority_created; drop index if exists minion_jobs_state; create index if not exists minion_jobs_state_priority_id on minion_jobs (state, priority desc, id); -- 7 up alter table minion_workers add column status text not null check(json_valid(status) and json_type(status) = 'object') default '{}'; -- 8 up create table if not exists minion_locks ( id integer not null primary key autoincrement, name text not null, expires text not null ); create index if not exists minion_locks_name_expires on minion_locks (name, expires); alter table minion_jobs add column notes text not null check(json_valid(notes) and json_type(notes) = 'object') default '{}'; -- 8 down drop table if exists minion_locks;