File Coverage

blib/lib/Net/Twitter/Role/RetryOnError.pm
Criterion Covered Total %
statement 9 9 100.0
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 12 12 100.0


line stmt bran cond sub pod time code
1             package Net::Twitter::Role::RetryOnError;
2             $Net::Twitter::Role::RetryOnError::VERSION = '4.01010';
3 1     1   718 use Moose::Role;
  1         1  
  1         9  
4 1     1   3743 use namespace::autoclean;
  1         2  
  1         10  
5 1     1   581 use Time::HiRes;
  1         1250  
  1         5  
6              
7             requires '_send_request';
8              
9             =head1 NAME
10              
11             Net::Twitter::Role::RetryOnError - Retry Twitter API calls on error
12              
13             =head1 VERSION
14              
15             version 4.01010
16              
17             =head1 SYNOPSIS
18              
19             use Net::Twitter;
20             $nt = Net::Twitter->new(
21             traits => ['API::RESTv1_1', 'RetryOnError']
22             max_retries => 3,
23             );
24              
25             =head1 DESCRIPTION
26              
27             Temporary errors are not uncommon when calling the Twitter API. When applied
28             to L<Net::Twitter> this role will provide automatic retries of API calls in a very
29             configurable way.
30              
31             It only retries when the response status code is E<gt>= 500. Other error codes
32             indicate a permanent error. If the maximum number of retries is reached,
33             without success, an exception is thrown, as usual.
34              
35              
36             =head1 OPTIONS
37              
38             This role adds the following options to C<new>:
39              
40             =over 4
41              
42             =item initial_retry_delay
43              
44             A floating point number specifying the initial delay, after an error, before
45             retrying. Default: 0.25 (250 milliseconds).
46              
47             =cut
48              
49             has initial_retry_delay => (
50             is => 'rw',
51             isa => 'Num',
52             default => 0.250, # 250 milliseconds
53             );
54              
55             =item max_retry_delay
56              
57             A floating point number specifying the maximum delay between retries. Default: 4.0
58              
59             =cut
60              
61             has max_retry_delay => (
62             is => 'rw',
63             isa => 'Num',
64             default => 4.0, # 4 seconds
65             );
66              
67             =item retry_delay_multiplier
68              
69             On the second and subsequent retries, a new delay is calculated by multiplying the previous
70             delay by C<retry_delay_multiplier>. Default: 2.0
71              
72             =cut
73              
74             has retry_delay_multiplier => (
75             is => 'rw',
76             isa => 'Num',
77             default => 2, # double the prior delay
78             );
79              
80             =item max_retries
81              
82             The maximum number of consecutive retries before giving up and throwing an exception.
83             If set to 0, it the API call will be retried indefinitely. Default 5.
84              
85             =cut
86              
87             has max_retries => (
88             is => 'rw',
89             isa => 'Int',
90             default => 5, # 0 = try forever
91             );
92              
93             =item retry_delay_code
94              
95             A code reference that will be called to handle the delay. It is passed a
96             single argument: a floating point number specifying the number of seconds to
97             delay. By default, L<Time::HiRes/sleep> is called.
98              
99             If you're using a non-blocking user agent, like L<Coro::LWP>, you should use
100             this option to provide a non-blocking delay.
101              
102             =cut
103              
104             has retry_delay_code => (
105             is => 'rw',
106             isa => 'CodeRef',
107             default => sub {
108             sub { Time::HiRes::sleep(shift) };
109             },
110             );
111              
112             =back
113              
114             =cut
115              
116             around _send_request => sub {
117             my ( $orig, $self, $msg ) = @_;
118              
119             my $is_oauth = do {
120             my $auth_header = $msg->header('authorization');
121             $auth_header && $auth_header =~ /^OAuth /;
122             };
123              
124             my $delay = $self->initial_retry_delay;
125             my $retries = $self->max_retries;
126             while () {
127             my $res = $self->$orig($msg);
128              
129             return $res if $res->is_success || $retries-- == 0 || $res->code < 500;
130              
131             $self->retry_delay_code->($delay);
132             $delay *= $self->retry_delay_multiplier;
133             $delay = $self->max_retry_delay if $delay > $self->max_retry_delay;
134              
135             # If this is an OAuth request, we need a new Authorization header
136             # (the nonce may be invalid, now).
137             $self->_add_authorization_header($msg) if $is_oauth;
138             }
139             };
140              
141              
142             1;
143              
144             __END__
145              
146             =head1 AUTHOR
147              
148             Marc Mims <marc@questright.com>
149              
150             =head1 COPYRIGHT
151              
152             Copyright (c) 2010 Marc Mims
153              
154             =head1 LICENSE
155              
156             This library is free software and may be distributed under the same terms as perl itself.
157              
158             =cut