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.01042';
3 1     1   899 use Moose::Role;
  1         2  
  1         9  
4 1     1   3713 use namespace::autoclean;
  1         1  
  1         9  
5 1     1   579 use Time::HiRes;
  1         1160  
  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.01042
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) 2016 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
159