File Coverage

blib/lib/Resque/Failures.pm
Criterion Covered Total %
statement 9 52 17.3
branch 0 10 0.0
condition 0 21 0.0
subroutine 3 12 25.0
pod 8 8 100.0
total 20 103 19.4


line stmt bran cond sub pod time code
1             package Resque::Failures;
2             # ABSTRACT: Class for managing Resque failures
3             $Resque::Failures::VERSION = '0.44';
4 9     9   92 use Moose;
  9         19  
  9         72  
5             with 'Resque::Encoder';
6 9     9   66872 use Class::Load qw(load_class);
  9         19  
  9         707  
7 9     9   60 use Carp;
  9         17  
  9         10520  
8              
9             has resque => (
10             is => 'ro',
11             required => 1,
12             handles => [qw/ redis key /]
13             );
14              
15             has failure_class => (
16             is => 'rw',
17             lazy => 1,
18             default => sub {
19             load_class('Resque::Failure::Redis');
20             'Resque::Failure::Redis';
21             },
22             trigger => sub {
23             my ( $self, $class ) = @_;
24             load_class($class);
25             }
26             );
27              
28             sub throw {
29 0     0 1   my $self = shift;
30 0           my $e = $self->create(@_);
31 0           $e->save;
32             }
33              
34             sub create {
35 0     0 1   my $self = shift;
36 0           $self->failure_class->new( @_, resque => $self->resque );
37             }
38              
39             sub count {
40 0     0 1   my $self = shift;
41 0           $self->redis->llen($self->key('failed'));
42             }
43              
44             sub all {
45 0     0 1   my ( $self, $start, $count ) = @_;
46 0   0       my $all = $self->resque->list_range(
      0        
47             $self->key('failed'), $start||0, $count||-1
48             );
49 0           $_ = $self->encoder->decode( $_ ) for @$all;
50 0 0         return wantarray ? @$all : $all;
51             }
52              
53             sub clear {
54 0     0 1   my $self = shift;
55 0           $self->redis->del($self->key('failed'));
56             }
57              
58             sub requeue {
59 0     0 1   my ( $self, $index ) = @_;
60 0           my ($item) = $self->all($index, 1);
61 0           $item->{retried_at} = DateTime->now->strftime("%Y/%m/%d %H:%M:%S");
62 0           $self->redis->lset(
63             $self->key('failed'), $index,
64             $self->encoder->encode($item)
65             );
66 0           $self->_requeue($item);
67             }
68              
69             sub _requeue {
70 0     0     my ( $self, $item, $queue ) = @_;
71 0   0       $self->resque->push( $queue || $item->{queue} => $item->{payload} );
72             }
73              
74             sub remove {
75 0     0 1   my ( $self, $index ) = @_;
76 0           my $id = rand(0xffffff);
77 0           my $key = $self->key('failed');
78 0           $self->redis->multi;
79 0           $self->redis->lset( $key, $index, $id);
80 0           $self->redis->lrem( $key, 1, $id );
81 0           $self->redis->exec;
82             }
83              
84             sub mass_remove {
85 0     0 1   my ( $self, %opt ) = @_;
86 0   0       $opt{limit} ||= $self->count || return 0;
      0        
87              
88 0 0 0       for (qw/queue error class args/) { $opt{$_} = qr/$opt{$_}/ if $opt{$_} && not ref $opt{$_} }
  0            
89              
90 0           my $key = $self->key('failed');
91 0           my $enc = $self->encoder;
92              
93 0           my ( $count, $rem ) = ( 0, 0 );
94 0           while ( my $encoded_item = $self->redis->lpop($key) ) {
95 0           my $item = $enc->decode($encoded_item);
96              
97             my $match = (!$opt{queue} && !$opt{error} && !$opt{class} && !$opt{args})
98             || (
99             (!$opt{queue} || $item->{queue} =~ $opt{queue})
100             && (!$opt{error} || $item->{error} =~ $opt{error})
101             && (!$opt{class} || $item->{payload}{class} =~ $opt{class})
102             && (!$opt{args} || $enc->encode($item->{payload}{args}) =~ $opt{args})
103 0   0       );
104              
105 0 0 0       if ( $match ) { $rem++; $self->_requeue($item, $opt{requeue_to}) if $opt{requeue} || $opt{requeue_to} }
  0 0          
  0            
106 0           else { $self->redis->rpush( $key => $encoded_item ) }
107              
108 0 0         last if ++$count >= $opt{limit};
109             }
110              
111 0           $rem;
112             }
113              
114             __PACKAGE__->meta->make_immutable();
115              
116             __END__
117              
118             =pod
119              
120             =encoding UTF-8
121              
122             =head1 NAME
123              
124             Resque::Failures - Class for managing Resque failures
125              
126             =head1 VERSION
127              
128             version 0.44
129              
130             =head1 ATTRIBUTES
131              
132             =head2 resque
133              
134             Accessor to the Resque object.
135              
136             =head2 failure_class
137              
138             Name of a class consuming the role 'Resque::Failure'.
139             By default: Resque::Failure::Redis
140              
141             =head1 METHODS
142              
143             =head2 throw
144              
145             create() a failure on the failure_class() and save() it.
146              
147             $failures->throw( %job_description_hash );
148              
149             See L<Resque> code for a usage example.
150              
151             =head2 create
152              
153             Create a new failure on the failure_class() backend.
154              
155             $failures->create( ... );
156              
157             =head2 count
158              
159             How many failures are in the resque system.
160              
161             my $count = $failures->count();
162              
163             =head2 all
164              
165             Return a range of failures (or an arrayref in scalar context)
166             in the same way Resque::peek() does for jobs.
167              
168             my @all = $failures->all; # get all failed jobs
169             my @some = $failures->all(10, 10); # get failure 10 to 20
170              
171             =head2 clear
172              
173             Remove all failures.
174              
175             $failures->clear();
176              
177             =head2 requeue
178              
179             Requeue by index number.
180              
181             Failure will be updated to note retried date.
182              
183             $failures->requeue( $index );
184              
185             =head2 remove
186              
187             Remove failure by index number in failures queue.
188              
189             Please note that, when you remove some index, all
190             sucesive ones will move left, so index will decrese
191             one. If you want to remove several ones start removing
192             from the rightmost one.
193              
194             $failures->remove( $index );
195              
196             =head2 mass_remove
197              
198             Remove and optionally requeue all or matching failed jobs. Errors that happen
199             after this method is fired will remain untouched.
200              
201             Filters, if present, are useful to select failed jobs and should be regexes or
202             strings that will be matched against any of the following failed job field:
203              
204             queue: the queue where job had failed
205             class: the job class
206             error: the error string
207             args: a JSON representation of the job arguments
208              
209             By default, all matching jobs will be deleted but the ones that
210             doesn't match will be placed back at the end of the failed jobs.
211              
212             The behavior can be modified with the following options:
213              
214             requeue: requeue matching jobs after being removed.
215             requeue_to: Requeued jobs will be placed on this queue instead of the original one. This option implies requeue.
216              
217             Example
218              
219             # Remove and requeue all failed jobs from queue 'test_queue' of class My::Job::Class
220             $resque->failures->mass_remove(
221             queue => 'test_queue',
222             class => qr/^My::Job::Class$/,
223             requeue => 1
224             );
225              
226             =head1 AUTHOR
227              
228             Diego Kuperman <diego@freekeylabs.com>
229              
230             =head1 COPYRIGHT AND LICENSE
231              
232             This software is copyright (c) 2021 by Diego Kuperman.
233              
234             This is free software; you can redistribute it and/or modify it under
235             the same terms as the Perl 5 programming language system itself.
236              
237             =cut