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