line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# You may distribute under the terms of either the GNU General Public License |
2
|
|
|
|
|
|
|
# or the Artistic License (the same terms as Perl itself) |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# (C) Paul Evans, 2014-2015 -- leonerd@leonerd.org.uk |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package IO::Async::Resolver::StupidCache; |
7
|
|
|
|
|
|
|
|
8
|
3
|
|
|
3
|
|
117591
|
use strict; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
72
|
|
9
|
3
|
|
|
3
|
|
11
|
use warnings; |
|
3
|
|
|
|
|
3
|
|
|
3
|
|
|
|
|
65
|
|
10
|
3
|
|
|
3
|
|
15
|
use base qw( IO::Async::Notifier ); |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
1547
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $VERSION = '0.04'; |
13
|
|
|
|
|
|
|
|
14
|
3
|
|
|
3
|
|
16196
|
use IO::Async::Resolver; |
|
3
|
|
|
|
|
114789
|
|
|
3
|
|
|
|
|
103
|
|
15
|
|
|
|
|
|
|
|
16
|
3
|
|
|
3
|
|
22
|
use Future 0.30; # ->without_cancel |
|
3
|
|
|
|
|
45
|
|
|
3
|
|
|
|
|
67
|
|
17
|
|
|
|
|
|
|
|
18
|
3
|
|
|
3
|
|
12
|
use Struct::Dumb qw( readonly_struct ); |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
13
|
|
19
|
|
|
|
|
|
|
readonly_struct CacheEntry => [qw( future expires )]; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 NAME |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
C - a trivial caching layer around an C |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 SYNOPSIS |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
use IO::Async::Loop 0.62; |
28
|
|
|
|
|
|
|
use IO::Async::Resolver::StupidCache; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
my $loop = IO::Async::Loop->new; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# Wrap the existing resolver in a cache |
33
|
|
|
|
|
|
|
$loop->set_resolver( |
34
|
|
|
|
|
|
|
IO::Async::Resolver::StupidCache->new( source => $loop->resolver ) |
35
|
|
|
|
|
|
|
); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# $loop->resolve requests will now be cached |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head1 DESCRIPTION |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
This object class provides a wrapper around another L |
42
|
|
|
|
|
|
|
instance, which applies a simple caching layer to avoid making identical |
43
|
|
|
|
|
|
|
lookups. This can be useful, for example, when performing a large number of |
44
|
|
|
|
|
|
|
HTTP requests to the same host or a small set of hosts, or other cases where |
45
|
|
|
|
|
|
|
it is expected that the same few resolver queries will be made over and over. |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
This is called a "stupid" cache because it is made without awareness of TTL |
48
|
|
|
|
|
|
|
values or other cache-relevant information that may be provided by DNS or |
49
|
|
|
|
|
|
|
other resolve methods. As such, it should not be relied upon to give |
50
|
|
|
|
|
|
|
always-accurate answers. |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=cut |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=head1 PARAMETERS |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
The following named parameters may be passed to C or C: |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=over 8 |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=item source => IO::Async::Resolver |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
Optional. The source of the cache data. If not supplied, a new |
63
|
|
|
|
|
|
|
C instance will be constructed. |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=item ttl => INT |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
Optional. Time-to-live of cache entries in seconds. If not supplied a default |
68
|
|
|
|
|
|
|
of 5 minutes will apply. |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=item max_size => INT |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
Optional. Maximum number of entries to keep in the cache. Entries will be |
73
|
|
|
|
|
|
|
evicted at random over this limit. If not supplied a default of 1000 entries |
74
|
|
|
|
|
|
|
will apply. |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=back |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
=cut |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
sub _init |
81
|
|
|
|
|
|
|
{ |
82
|
2
|
|
|
2
|
|
6545
|
my $self = shift; |
83
|
2
|
|
|
|
|
4
|
my ( $params ) = @_; |
84
|
|
|
|
|
|
|
|
85
|
2
|
|
33
|
|
|
11
|
$params->{source} ||= IO::Async::Resolver->new; |
86
|
|
|
|
|
|
|
|
87
|
2
|
|
50
|
|
|
10
|
$params->{ttl} ||= 300; |
88
|
2
|
|
50
|
|
|
11
|
$params->{max_size} ||= 1000; |
89
|
|
|
|
|
|
|
|
90
|
2
|
|
|
|
|
10
|
$self->SUPER::_init( $params ); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub configure |
94
|
|
|
|
|
|
|
{ |
95
|
2
|
|
|
2
|
1
|
11
|
my $self = shift; |
96
|
2
|
|
|
|
|
6
|
my %params = @_; |
97
|
|
|
|
|
|
|
|
98
|
2
|
|
|
|
|
4
|
foreach (qw( source ttl max_size )) { |
99
|
6
|
50
|
|
|
|
22
|
$self->{$_} = delete $params{$_} if exists $params{$_}; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
2
|
|
|
|
|
10
|
$self->SUPER::configure( %params ); |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=head1 METHODS |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
The following methods documented with a trailing call to C<< ->get >> return |
108
|
|
|
|
|
|
|
L instances. |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=cut |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=head2 $resolver = $cache->source |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
Returns the source resolver |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
=cut |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub source |
119
|
|
|
|
|
|
|
{ |
120
|
4
|
|
|
4
|
1
|
5
|
my $self = shift; |
121
|
4
|
|
|
|
|
12
|
return $self->{source}; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
=head2 @result = $cache->resolve( %args )->get |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=head2 @addrs = $cache->getaddrinfo( %args )->get |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=head2 ( $host, $service ) = $cache->getnameinfo( %args )->get |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
These methods perform identically to the base C class, |
131
|
|
|
|
|
|
|
except that the results are cached. |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
Returned C are created with the C method, so that |
134
|
|
|
|
|
|
|
multiple concurrent waiters are shielded from cancellation by one another. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=cut |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub resolve |
139
|
|
|
|
|
|
|
{ |
140
|
13
|
|
|
13
|
1
|
4602
|
my $self = shift; |
141
|
13
|
|
|
|
|
30
|
my %args = @_; |
142
|
|
|
|
|
|
|
|
143
|
13
|
|
|
|
|
10
|
my $type = $args{type}; |
144
|
13
|
|
|
|
|
12
|
my $data = $args{data}; |
145
|
|
|
|
|
|
|
|
146
|
13
|
|
100
|
|
|
49
|
my $cache = $self->{cache} ||= {}; |
147
|
|
|
|
|
|
|
|
148
|
13
|
|
|
|
|
26
|
my $now = $self->loop->time; |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# At the current time, all the resolvers use a flat list of non-ref scalars |
151
|
|
|
|
|
|
|
# as arguments. We can simply flatten this to a string to use as our cache key |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# getaddrinfo needs special handling as it's a name/value pair list; accept |
154
|
|
|
|
|
|
|
# also getaddrinfo_hash |
155
|
|
|
|
|
|
|
my $cachekey = join "\0", ( $type =~ m/^getaddrinfo(?:_hash)?$/ ) |
156
|
13
|
100
|
|
|
|
109
|
? do { my %data = @$data; $type, map { $_ => $data{$_} } sort keys %data } |
|
10
|
|
|
|
|
20
|
|
|
10
|
|
|
|
|
27
|
|
|
23
|
|
|
|
|
51
|
|
157
|
|
|
|
|
|
|
: ( $type, @$data ); |
158
|
|
|
|
|
|
|
|
159
|
13
|
100
|
|
|
|
76
|
if( my $entry = $cache->{$cachekey} ) { |
160
|
9
|
50
|
|
|
|
39
|
return $entry->future->without_cancel if $entry->expires > $now; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
4
|
|
|
|
|
8
|
my $f = $self->source->resolve( %args ); |
164
|
|
|
|
|
|
|
|
165
|
4
|
|
|
|
|
141
|
$cache->{$cachekey} = CacheEntry( $f, $now + $self->{ttl} ); |
166
|
|
|
|
|
|
|
|
167
|
4
|
|
|
|
|
36
|
while( scalar( keys %$cache ) > $self->{max_size} ) { |
168
|
0
|
|
|
|
|
0
|
delete $cache->{ ( keys %$cache )[rand keys %$cache] }; |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
|
171
|
4
|
|
|
|
|
12
|
return $f->without_cancel; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# Resolver's ->getaddrinfo and ->getnameinfo convenience methods are useful to |
175
|
|
|
|
|
|
|
# have here, but are implemented in terms of the basic ->resolve. |
176
|
|
|
|
|
|
|
# We can cheat and just import those methods directly here |
177
|
|
|
|
|
|
|
*getaddrinfo = \&IO::Async::Resolver::getaddrinfo; |
178
|
|
|
|
|
|
|
*getnameinfo = \&IO::Async::Resolver::getnameinfo; |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=head1 AUTHOR |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
Paul Evans |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=cut |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
0x55AA; |