File Coverage

blib/lib/Proc/BackOff.pm
Criterion Covered Total %
statement 40 45 88.8
branch 10 18 55.5
condition 4 9 44.4
subroutine 8 11 72.7
pod 8 8 100.0
total 70 91 76.9


line stmt bran cond sub pod time code
1             package Proc::BackOff;
2              
3             # Inheritance
4 5     5   33391 use base qw( Class::Accessor );
  5         9  
  5         5303  
5              
6             # Set up get/set fields
7             __PACKAGE__->mk_accessors( 'max_timeout',
8             'failure_count', # current failure count
9             'failure_start', # time that inital failure
10             # - started. Recorded for
11             # - future classes
12             'failure_time', # time of last failure
13             'failure_over', # when will the failure be over?
14             'backOff_in_progress', # back off is in progress.
15             'init', # was this initalized?
16             );
17              
18             # standard pragmas
19 5     5   15578 use warnings;
  5         14  
  5         183  
20 5     5   27 use strict;
  5         30  
  5         3086  
21              
22             # standard perl modules
23              
24             # CPAN & others
25              
26             our $VERSION = '0.02';
27              
28             =head1 NAME
29              
30             Proc::BackOff
31              
32             =head1 SYNOPSIS
33              
34             Usage:
35              
36             use Proc::BackOff::Linear;
37              
38             my $obj = Proc::BackOff::Linear->new( { slope => 5 } );
39              
40             while ( 1 ) {
41             # delay will return
42             # 0 : No delay needed.
43             # N : or the number of seconds until back off is completed.
44              
45             sleep $obj->delay() if $obj->delay();
46             # or
47             $obj->sleep();
48              
49             if ( do_attempt() ) {
50             # success
51             $obj->success(); # passing success to Proc::BackOff will reset
52             # Proc::BackOff
53             } else {
54             # failure
55             $obj->failure(); # passing failure will instruct Proc::BackOff to
56             # increment the time to back off
57             }
58              
59             # 100 failures in a row, time to exit
60             die "complete failure" if $obj->failure_count() > 100;
61             }
62              
63             $obj->reset(); # reset back the same state as it was new.
64              
65             =head1 DESCRIPTION
66              
67             Proc::BackOff is a base module meant to be directly inherited from and then
68             modified by overloading the calculate_back_off object method.
69              
70             Use: Proc::BackOff::Linear, Proc::BackOff::Random, or Proc::BackOff::Exponential.
71              
72             Any success C<$obj-Esuccess()> will result, in the back off being removed.
73              
74             =head1 METHODS
75              
76             =head2 new()
77              
78             This is for internal use only.
79              
80             Do not call this function, call new from:
81             L,
82             L, or L.
83              
84             =cut
85              
86             sub new {
87 5     5 1 15 my ( $proto, $fields ) = @_;
88              
89 5   33     33 my $class = ref $proto || $proto;
90              
91 5 50       24 $fields = {} unless defined $fields;
92              
93             # make a copy of $fields.
94 5         36 my $obj = bless {%$fields}, $class;
95              
96             # reset uses max_timeout, so max_timeout must be set first.
97 5 50       40 $obj->max_timeout(0) unless defined $obj->max_timeout();
98              
99 5         206 $obj->reset();
100              
101 5         53 return $obj;
102             }
103              
104             =head2 delay()
105              
106             Delay will return the following
107              
108             > 0, number of seconds until the delay is over
109             0 delay is up. Meaning that you should do your next attempt.
110              
111             =cut
112              
113             sub delay {
114 50     50 1 492 my $self = shift;
115              
116 50 50       105 return 0 if $self->failure_time() == 0;
117 50 50       504 return 0 if $self->backOff_in_progress() == 0;
118              
119             # current time - end of timeout
120 50         386 my $time = time; # to help with debugging
121 50         110 my $timeLeft = $self->failure_over() - $time;
122              
123             # $timeLeft < 0 we are done
124             # $timeLeft = 0 we are done
125             # $timeLeft > 0 we have time remaining
126              
127 50 100       490 return $timeLeft > 0 ? $timeLeft : 0;
128             }
129              
130             =head2 sleep()
131              
132             This is a short cut for:
133              
134             sleep $obj->delay() if $obj->delay();
135              
136             =cut
137              
138             sub sleep {
139 0     0 1 0 my $self = shift;
140              
141 0 0       0 sleep $self->delay() if $self->delay();
142             }
143              
144              
145             =head2 success()
146              
147             Success will clear Proc::BackOff delay.
148              
149             =cut
150              
151             sub success {
152 0     0 1 0 my $self = shift;
153              
154 0         0 $self->reset();
155             }
156              
157             =head2 reset()
158              
159             Simply just resets $obj back to a state in which no "backing off" exists.
160              
161             =cut
162              
163             sub reset {
164 5     5 1 90 my $self = shift;
165              
166 5         25 $self->failure_count(0);
167 5         65 $self->failure_over(0);
168 5         60 $self->failure_time(0);
169 5         66 $self->failure_start(0);
170 5         67 $self->backOff_in_progress(0);
171             }
172              
173             =head2 failure()
174              
175             Failure will indicicate to the object to increment the current BackOff time.
176              
177             The calculate_back_off function is called to get the time in seconds to wait.
178              
179             The time waited is time+calculated_back_off time, however it is capped by
180             $self->max_timeout().
181              
182             =cut
183              
184             sub failure {
185 50     50 1 27266 my $self = shift;
186              
187 50         719 $self->backOff_in_progress(1);
188 50         555 $self->failure_count( $self->failure_count() + 1 );
189              
190 50         741 my $time = time;
191 50 100       114 $self->failure_start($time) if $self->failure_start() == 0;
192 50         543 $self->failure_time($time);
193              
194 50         1110 my $moreTime = $self->calculate_back_off();
195              
196             # if - max_timeout > 0, then we cap timeout at max_timeout
197 50 50 33     150 $moreTime = $self->max_timeout()
198             if ( $self->max_timeout() > 0 && $moreTime > $self->max_timeout() );
199             # else - To infinity and beyond! -Buzz Lightyear
200              
201 50         562 $self->failure_over($time+$moreTime);
202             }
203              
204             =head2 valid_number_check()
205              
206             Is this a number we can use?
207              
208             1
209             1.234
210             'count'
211              
212             are valid values.
213              
214             =cut
215              
216             sub valid_number_check {
217 12     12 1 319 my $self = shift;
218 12 50       31 return undef unless defined $_[0];
219              
220             # Regex from: http://p3m.org/faq/C3/Q3.html
221 12   66     134 return $_[0] =~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/ || $_[0] eq 'count';
222             }
223              
224              
225             =head2 calculate_back_off()
226              
227             Returns the new back off value.
228              
229             This is the key function you want to overload if you wish to create your own
230             BackOff library.
231              
232             The following functions can be used.
233              
234             =over 4
235              
236             =item * $self->failure_count()
237              
238             The current number of times, that failure has been sequentially called.
239              
240             =item * $self->failure_start()
241              
242             When as reported by time in seconds from epoch was failure first called
243              
244             =item * $self->failure_time()
245              
246             When was the last failure reported ie, $self->failure() called.
247              
248             =item * $self->failure_over()
249              
250             When in time since epoch will the failure be over.
251              
252             =back
253              
254             =cut
255              
256             sub calculate_back_off {
257 0     0 1   die "Virtual Method";
258             }
259              
260             # subroutines automatically created by mk_accessors
261             # Class
262              
263             =head2 backOff_in_progress()
264              
265             returns 1 if a back off is in progress
266              
267             returns 0 if a back off is not in progress.
268              
269             The difference between backOff_in_progress and delay() > 0, is that at the end
270             of a timeout, delay() will return 0, while the backoff will still be in
271             progress.
272              
273             =head2 max_timeout()
274              
275             Subroutine automatically created by mk_accessors.
276              
277             Get $obj->max_timeout()
278              
279             Set $obj->max_timeout( 60*60 ) ; # 60 * 60 seconds = 1 hour
280              
281             The Maximum amount of time to wait.
282              
283             A max_timeout value of zero, means there is no Maximum.
284              
285             =head2 failure_time()
286              
287             Subroutine automatically created by mk_accessors.
288              
289             When was $obj->failure() last called? Time in seconds since epoch.
290              
291             Get $obj->failure_time()
292              
293             This variable is not meant to be set by the end user. This variable is set when
294             $obj->failure() is called.
295              
296             =head2 failure_over()
297              
298             When in seconds since epoch is the failure_over()?
299              
300             This is used internally by object method delay();
301              
302             =cut
303              
304             1;
305              
306             =head1 Inheritance
307              
308             I have included an exponential, linear, and random back off. You can use any of
309             these sub classes to make a new back off library. Please consider sending me
310             any additional BackOff functions, so that I may include it for others to use.
311              
312             =head1 Notes
313              
314             Please send me any bugfixes or corrections. Even spelling correctins :).
315              
316             Please file any bugs with:
317              
318             L
319              
320             =head1 Changes
321              
322             0.02 2007-08-12 -- Daniel Lo
323             - Documentation fixes. No code changes.
324              
325             0.01 2007-04-17 -- Daniel Lo
326             - Initial version
327              
328             =head1 AUTHOR
329              
330             Daniel Lo
331              
332             =head1 LICENSE
333              
334             Copyright (C) PictureTrail Inc. 1999-2007
335             Santa Clara, California, United States of America.
336              
337             This code is released to the public for public use under Perl's Artisitic
338             licence.
339              
340             =cut