File Coverage

blib/lib/Mojo/Redis.pm
Criterion Covered Total %
statement 62 65 95.3
branch 15 24 62.5
condition 17 25 68.0
subroutine 17 18 94.4
pod 4 4 100.0
total 115 136 84.5


line stmt bran cond sub pod time code
1             package Mojo::Redis;
2 18     18   1749765 use Mojo::Base 'Mojo::EventEmitter';
  18         31  
  18         113  
3              
4 18     18   36498 use Mojo::URL;
  18         2684033  
  18         131  
5 18     18   9005 use Mojo::Redis::Connection;
  18         71  
  18         122  
6 18     18   8634 use Mojo::Redis::Cache;
  18         46  
  18         79  
7 18     18   7772 use Mojo::Redis::Cursor;
  18         41  
  18         86  
8 18     18   10739 use Mojo::Redis::Database;
  18         51  
  18         100  
9 18     18   8590 use Mojo::Redis::PubSub;
  18         42  
  18         89  
10 18     18   661 use Scalar::Util 'blessed';
  18         27  
  18         21155  
11              
12             our $VERSION = '3.31';
13              
14             $ENV{MOJO_REDIS_URL} ||= 'redis://localhost:6379';
15              
16             has encoding => 'UTF-8';
17             has max_connections => 5;
18              
19             has protocol_class => do {
20             my $class = $ENV{MOJO_REDIS_PROTOCOL};
21             $class ||= eval { require Protocol::Redis::XS; Protocol::Redis::XS->VERSION('0.06'); 'Protocol::Redis::XS' };
22             $class ||= 'Protocol::Redis::Faster';
23             eval "require $class; 1" or die $@;
24             $class;
25             };
26              
27             has pubsub => sub {
28             my $self = shift;
29             my $pubsub = Mojo::Redis::PubSub->new(redis => $self);
30             Scalar::Util::weaken($pubsub->{redis});
31             return $pubsub;
32             };
33              
34             has url => sub { Mojo::URL->new($ENV{MOJO_REDIS_URL}) };
35              
36 1     1 1 23 sub cache { Mojo::Redis::Cache->new(redis => shift, @_) }
37 0 0   0 1 0 sub cursor { Mojo::Redis::Cursor->new(redis => shift, command => [@_ ? @_ : (scan => 0)]) }
38 12     12 1 5518 sub db { Mojo::Redis::Database->new(redis => shift) }
39              
40             sub new {
41 12     12 1 1265800 my $class = shift;
42 12 100 100     115 return $class->SUPER::new(@_) unless @_ % 2 and ref $_[0] ne 'HASH';
43 7         16 my $url = shift;
44 7 100 66     87 $url = Mojo::URL->new($url) unless blessed $url and $url->isa('Mojo::URL');
45 7         1177 return $class->SUPER::new(url => $url, @_);
46             }
47              
48             sub _connection {
49 16     16   48 my ($self, %args) = @_;
50              
51 16   66     97 $args{ioloop} ||= Mojo::IOLoop->singleton;
52 16         57 my %tls_args;
53 16 100       29 if (my %url_args = %{$self->url->query->to_hash}) {
  16         40  
54 1         146 @tls_args{qw/ tls tls_ca tls_cert tls_key /} = @url_args{qw/ tls ca cert key /};
55 1         46 delete @tls_args{grep !defined $tls_args{$_}, keys %tls_args};
56 1         8 my %tls_options = map { ($_, $url_args{$_}) } grep /^SSL_/, keys %url_args;
  0         0  
57             $tls_options{$_} = [split /,/, $tls_options{$_}]
58 1         3 foreach grep /^SSL_(psk|(npn|alpn)_protocols)\z/, keys %tls_options;
59 1 50       2 $tls_args{tls_options} = \%tls_options if %tls_options;
60 1 50 0     3 $tls_args{tls} //= 1 if %tls_args;
61             }
62 16         686 my $conn = Mojo::Redis::Connection->new(
63             encoding => $self->encoding,
64             protocol => $self->protocol_class->new(api => 1),
65             url => $self->url->clone,
66             %args, %tls_args
67             );
68              
69 16         1719 Scalar::Util::weaken($self);
70 16     5   115 $conn->on(connect => sub { $self->emit(connection => $_[0]) });
  5         60  
71 16         257 $conn;
72             }
73              
74             sub _blocking_connection {
75 5     5   3430 my $self = shift->_fork_safety;
76              
77             # Existing connection
78 5         10 my $conn = $self->{blocking_connection};
79 5 50 66     16 return $conn->encoding($self->encoding) if $conn and $conn->is_connected;
80              
81             # New connection
82 5 100       20 return $self->{blocking_connection} = $self->_connection(ioloop => $conn ? $conn->ioloop : Mojo::IOLoop->new);
83             }
84              
85             sub _dequeue {
86 10     10   72 my $self = shift->_fork_safety;
87              
88             # Exsting connection
89 10 0       17 while (my $conn = shift @{$self->{queue} || []}) { return $conn->encoding($self->encoding) if $conn->is_connected }
  0 100       0  
  10         50  
90              
91             # New connection
92 10         29 return $self->_connection;
93             }
94              
95             sub _enqueue {
96 10     10   17 my ($self, $conn) = @_;
97 10   100     45 my $queue = $self->{queue} ||= [];
98 10 50 66     36 push @$queue, $conn if $conn->is_connected and $conn->url eq $self->url and $conn->ioloop eq Mojo::IOLoop->singleton;
      66        
99 10         2188 shift @$queue while @$queue > $self->max_connections;
100             }
101              
102             sub _fork_safety {
103 15     15   22 my $self = shift;
104 15 50 66     145 delete @$self{qw(blocking_connection pid queue)} unless ($self->{pid} //= $$) eq $$; # Fork-safety
105 15         26 $self;
106             }
107              
108             1;
109              
110             =encoding utf8
111              
112             =head1 NAME
113              
114             Mojo::Redis - Redis driver based on Mojo::IOLoop
115              
116             =head1 SYNOPSIS
117              
118             =head2 Blocking
119              
120             use Mojo::Redis;
121             my $redis = Mojo::Redis->new;
122             $redis->db->set(foo => 42);
123             $redis->db->expire(foo => 600);
124             warn $redis->db->get('foo');
125              
126             =head2 Promises
127              
128             $redis->db->get_p("mykey")->then(sub {
129             print "mykey=$_[0]\n";
130             })->catch(sub {
131             warn "Could not fetch mykey: $_[0]";
132             })->wait;
133              
134             =head2 Pipelining
135              
136             Pipelining is built into the API by sending a lot of commands and then use
137             L to wait for all the responses.
138              
139             Mojo::Promise->all(
140             $db->set_p($key, 10),
141             $db->incrby_p($key, 9),
142             $db->incr_p($key),
143             $db->get_p($key),
144             $db->incr_p($key),
145             $db->get_p($key),
146             )->then(sub {
147             @res = map {@$_} @_;
148             })->wait;
149              
150             =head1 DESCRIPTION
151              
152             L is a Redis driver that use the L, which makes it
153             integrate easily with the L framework.
154              
155             It tries to mimic the same interface as L, L and
156             L, but the methods for talking to the database vary.
157              
158             This module is in no way compatible with the 1.xx version of C
159             and this version also tries to fix a lot of the confusing methods in
160             C related to pubsub.
161              
162             This module is currently EXPERIMENTAL, and bad design decisions will be fixed
163             without warning. Please report at
164             L if you find this module
165             useful, annoying or if you simply find bugs. Feedback can also be sent to
166             C.
167              
168             =head1 EVENTS
169              
170             =head2 connection
171              
172             $cb = $redis->on(connection => sub { my ($redis, $connection) = @_; });
173              
174             Emitted when L connects to the Redis.
175              
176             =head1 ATTRIBUTES
177              
178             =head2 encoding
179              
180             $str = $redis->encoding;
181             $redis = $redis->encoding("UTF-8");
182              
183             The value of this attribute will be passed on to
184             L when a new connection is created. This
185             means that updating this attribute will not change any connection that is
186             in use.
187              
188             Default value is "UTF-8".
189              
190             =head2 max_connections
191              
192             $int = $redis->max_connections;
193             $redis = $redis->max_connections(5);
194              
195             Maximum number of idle database handles to cache for future use, defaults to
196             5. (Default is subject to change)
197              
198             =head2 protocol_class
199              
200             $str = $redis->protocol_class;
201             $redis = $redis->protocol_class("Protocol::Redis::XS");
202              
203             Default to L if the optional module is available and at
204             least version 0.06, or falls back to L.
205              
206             =head2 pubsub
207              
208             $pubsub = $redis->pubsub;
209              
210             Lazy builds an instance of L for this object, instead of
211             returning a new instance like L does.
212              
213             =head2 url
214              
215             $url = $redis->url;
216             $redis = $redis->url(Mojo::URL->new("redis://localhost/3"));
217              
218             Holds an instance of L that describes how to connect to the Redis server.
219              
220             =head1 METHODS
221              
222             =head2 db
223              
224             $db = $redis->db;
225              
226             Returns an instance of L.
227              
228             =head2 cache
229              
230             $cache = $redis->cache(%attrs);
231              
232             Returns an instance of L.
233              
234             =head2 cursor
235              
236             $cursor = $redis->cursor(@command);
237              
238             Returns an instance of L with
239             L set to the arguments passed. See
240             L. for possible commands.
241              
242             =head2 new
243              
244             $redis = Mojo::Redis->new("redis://localhost:6379/1");
245             $redis = Mojo::Redis->new(Mojo::URL->new->host("/tmp/redis.sock"));
246             $redis = Mojo::Redis->new(\%attrs);
247             $redis = Mojo::Redis->new(%attrs);
248              
249             # TLS options
250             $redis = Mojo::Redis->new("redis://localhost:6379/1?tls=1");
251             $redis = Mojo::Redis->new("redis://localhost:6379/1?ca=ca.pem&cert=cert.pem&key=key.pem&SSL_alpn_protocols=foo");
252              
253             Object constructor. Can coerce a string into a L and set L
254             if present.
255              
256             =head1 AUTHORS
257              
258             Jan Henning Thorsen - C
259              
260             Dan Book - C
261              
262             =head1 COPYRIGHT AND LICENSE
263              
264             Copyright (C) 2018, Jan Henning Thorsen.
265              
266             This program is free software, you can redistribute it and/or modify it under
267             the terms of the Artistic License version 2.0.
268              
269             =cut