File Coverage

blib/lib/Twitter/API/Trait/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 Twitter::API::Trait::RetryOnError;
2             # ABSTRACT: Automatically retry API calls on error
3             $Twitter::API::Trait::RetryOnError::VERSION = '1.0006';
4 1     1   527 use Moo::Role;
  1         2  
  1         6  
5 1     1   800 use Time::HiRes;
  1         1239  
  1         3  
6 1     1   93 use namespace::clean;
  1         3  
  1         7  
7              
8             #pod =attr initial_retry_delay
9             #pod
10             #pod Amount of time to delay before the initial retry. Specified in fractional
11             #pod seconds. Default: 0.25 (250ms).
12             #pod
13             #pod =cut
14              
15             has initial_retry_delay => (
16             is => 'rw',
17             default => sub { 0.250 }, # 250 milliseconds
18             );
19              
20             #pod =attr max_retry_delay
21             #pod
22             #pod Maximum delay between retries, specified in fractional seconds. Default: 4.0.
23             #pod
24             #pod =cut
25              
26             has max_retry_delay => (
27             is => 'rw',
28             default => sub { 4.0 }, # 4 seconds
29             );
30              
31             #pod =attr retry_delay_multiplier
32             #pod
33             #pod After the initial delay, the delay time is multiplied by this factor to
34             #pod progressively back off allowing more time for the transient condition to
35             #pod resolve. However, the delay never exceeds C<max_retry_delay>. Default: 2.0.
36             #pod
37             #pod =cut
38              
39             has retry_delay_multiplier => (
40             is => 'rw',
41             default => sub { 2 }, # double the prior delay
42             );
43              
44             #pod =attr max_retries
45             #pod
46             #pod Maximum number of times to retry on error. Default: 5.
47             #pod
48             #pod =cut
49              
50             has max_retries => (
51             is => 'rw',
52             default => sub { 5 }, # 0 = try forever
53             );
54              
55             #pod =attr retry_delay_code
56             #pod
57             #pod A coderef, called to implement a delay. It takes a single parameter, the number
58             #pod of seconds to delay. E.g., 0.25. The default implementation is simply:
59             #pod
60             #pod sub { Time::HiRes::sleep(shift) }
61             #pod
62             #pod =cut
63              
64             has retry_delay_code => (
65             is => 'rw',
66             default => sub {
67             sub { Time::HiRes::sleep(shift) };
68             },
69             );
70              
71             around send_request => sub {
72             my $orig = shift;
73             my $self = shift;
74             my ( $c ) = @_;
75              
76             my $msg = $c->http_request;
77             my $is_oauth = ( $msg->header('authorization') // '' ) =~ /^OAuth /;
78              
79             my $delay = $self->initial_retry_delay;
80             my $retries = $self->max_retries;
81             my $res;
82             while () {
83             $res = $self->$orig(@_);
84              
85             # return on success or permanent error
86             return $res if $res->code < 500 || $retries-- == 0;
87              
88             $self->retry_delay_code->($delay);
89             $delay *= $self->retry_delay_multiplier;
90             $delay = $self->max_retry_delay if $delay > $self->max_retry_delay;
91              
92             # If this is an OAuth request, we need a new Authorization header
93             # (the nonce may be invalid, now).
94             if ( $is_oauth ) {
95             $self->add_authorization($c);
96             }
97             }
98              
99             $res;
100             };
101              
102             1;
103              
104             __END__
105              
106             =pod
107              
108             =encoding UTF-8
109              
110             =head1 NAME
111              
112             Twitter::API::Trait::RetryOnError - Automatically retry API calls on error
113              
114             =head1 VERSION
115              
116             version 1.0006
117              
118             =head1 SYNOPSIS
119              
120             use Twitter::API;
121              
122             my $client = Twitter::API->new_with_options(
123             traits => [ qw/ApiMethods RetryOnError/ ],
124             %other_optons
125             );
126              
127             my $statuses = $client->home_timeline;
128              
129             =head1 DESCRIPTION
130              
131             With this trait applied, Twitter::API automatically retries API calls that
132             result in an HTTP status code of 500 or greater. These errors often indicate a
133             temporary problem, either on Twitter's end, locally, or somewhere in between.
134             By default, it retries up to 5 times. The initial retry is delayed by 250ms.
135             Additional retries double the delay time until the maximum delay is reached
136             (default: 4 seconds). Twitter::API throws a C<Twitter::API::Error> exception
137             when it receives a permanent error (HTTP status code below 500), or the maximum
138             number of retries has been reached.
139              
140             For non-blocking applications, set a suitable C<retry_delay_code> callback.
141             This attribute can also be used to provided retry logging.
142              
143             =head1 ATTRIBUTES
144              
145             =head2 initial_retry_delay
146              
147             Amount of time to delay before the initial retry. Specified in fractional
148             seconds. Default: 0.25 (250ms).
149              
150             =head2 max_retry_delay
151              
152             Maximum delay between retries, specified in fractional seconds. Default: 4.0.
153              
154             =head2 retry_delay_multiplier
155              
156             After the initial delay, the delay time is multiplied by this factor to
157             progressively back off allowing more time for the transient condition to
158             resolve. However, the delay never exceeds C<max_retry_delay>. Default: 2.0.
159              
160             =head2 max_retries
161              
162             Maximum number of times to retry on error. Default: 5.
163              
164             =head2 retry_delay_code
165              
166             A coderef, called to implement a delay. It takes a single parameter, the number
167             of seconds to delay. E.g., 0.25. The default implementation is simply:
168              
169             sub { Time::HiRes::sleep(shift) }
170              
171             =head1 AUTHOR
172              
173             Marc Mims <marc@questright.com>
174              
175             =head1 COPYRIGHT AND LICENSE
176              
177             This software is copyright (c) 2015-2021 by Marc Mims.
178              
179             This is free software; you can redistribute it and/or modify it under
180             the same terms as the Perl 5 programming language system itself.
181              
182             =cut