File Coverage

blib/lib/HealthCheck/Diagnostic/Redis.pm
Criterion Covered Total %
statement 58 60 96.6
branch 20 28 71.4
condition 5 12 41.6
subroutine 12 12 100.0
pod 5 5 100.0
total 100 117 85.4


line stmt bran cond sub pod time code
1             package HealthCheck::Diagnostic::Redis;
2              
3 2     2   346176 use strict;
  2         5  
  2         90  
4 2     2   14 use warnings;
  2         5  
  2         164  
5              
6 2     2   532 use parent 'HealthCheck::Diagnostic';
  2         381  
  2         16  
7              
8 2     2   13460 use Carp;
  2         10  
  2         175  
9 2     2   1598 use Redis::Fast;
  2         317545  
  2         81  
10 2     2   1279 use String::Random;
  2         7953  
  2         150  
11              
12             # ABSTRACT: Check for Redis connectivity and operations in HealthCheck
13 2     2   18 use version;
  2         4  
  2         18  
14             our $VERSION = 'v0.0.6'; # VERSION
15              
16             sub new {
17 2     2 1 2821 my ($class, @params) = @_;
18              
19             my %params = @params == 1 && ( ref $params[0] || '' ) eq 'HASH'
20 2 50 33     13 ? %{ $params[0] } : @params;
  0         0  
21              
22 2         14 return $class->SUPER::new(
23             id => 'redis',
24             label => 'redis',
25             %params,
26             );
27             }
28              
29             sub check {
30 8     8 1 174020 my ($self, %params) = @_;
31            
32             # Allow the diagnostic to be called as a class as well.
33 8 100       26 if ( ref $self ) {
34             $params{$_} = $self->{$_}
35 2         10 foreach grep { ! defined $params{$_} } keys %$self;
  6         19  
36             }
37            
38             # The host is the only required parameter.
39 8 100       212 croak "No host" unless $params{host};
40            
41 7         33 return $self->SUPER::check(%params);
42             }
43              
44             sub run {
45 7     7 1 147 my ($self, %params) = @_;
46              
47 7         16 my $host = $params{host};
48              
49 7         24 my $name = $params{name};
50 7 50       18 my $description = $name ? "$name ($host) Redis" : "$host Redis";
51              
52             # Add on the port if need be.
53 7 50       24 $host .= ':6379' unless $host =~ /:\d+$/;
54              
55             # Connect to the host...
56 7         12 my $redis;
57 7         11 local $@;
58 7         9 eval {
59 7         24 local $SIG{__DIE__};
60 7         26 $redis = Redis::Fast->new(
61             server => $host,
62              
63             # Attempt to reconnect up to 5 times every 1 second. It is common to
64             # need to reconnect when in a hiredis environment in particular.
65             reconnect => 5,
66             every => 1_000_000,
67              
68             # 5 second connect/read/write timeouts.
69             cnx_timeout => 5,
70             read_timeout => 5,
71             write_timeout => 5,
72             );
73             };
74             return {
75 7 100       161 status => 'CRITICAL',
76             info => "Error for $description: $@",
77             } if $@;
78              
79 6 50       16 unless ($redis->ping) {
80             return {
81 0         0 status => 'CRITICAL',
82             info => "Error for $description: Redis ping failed",
83             };
84             }
85              
86             # Attempt to get a result from the readability or writeability
87             # test.
88             my $res = $params{read_only}
89 6 100       59 ? $self->test_read_only( $redis, $description, %params )
90             : $self->test_read_write( $redis, $description, %params );
91              
92 6 100       35 return $res if ref $res eq 'HASH';
93             return {
94 4         28 status => 'OK',
95             info => "Successful connection for $description",
96             };
97             }
98              
99             sub test_read_only {
100 2     2 1 10 my ($self, $redis, $description, %params) = @_;
101              
102 2   33     23 my ($key, $error) = ($params{key_name}) || $redis->randomkey;
103             return {
104 2 50       9 status => 'CRITICAL',
105             info => sprintf( 'Error for %s: Failed getting random entry - %s',
106             $description,
107             $error,
108             ),
109             } if $error;
110              
111             # When there is no key, that means we don't have anything in the
112             # database. No need to ping on that.
113 2 0 33     8 return unless $key || $params{key_name};
114              
115 2         7 my $val = $redis->get( $key );
116             return {
117 2 100       23 status => 'CRITICAL',
118             info => sprintf( 'Error for %s: Failed reading value of key %s',
119             $description,
120             $key,
121             ),
122             } unless defined $val;
123             }
124              
125             sub test_read_write {
126 4     4 1 9 my ($self, $redis, $description, %params) = @_;
127 4   66     28 my $key = $params{key_name} || sprintf(
128             '_health_check_%s',
129             String::Random->new->randregex('[A-Z0-9]{24}'),
130             );
131              
132             # Do not overwrite anything in the database.
133             return {
134 4 100       746 status => 'CRITICAL',
135             info => sprintf( 'Error for %s: Cannot overwrite key %s',
136             $description,
137             $key,
138             ),
139             } if defined $redis->get( $key );
140              
141             # Set, get, and delete the temporary value. Also set an expiration
142             # date of 5 seconds after setting just in-case.
143 3         24 $redis->set( $key => 'temp', EX => 5 );
144 3         16 my $val = $redis->get( $key );
145 3         13 $redis->del( $key );
146              
147             return {
148 3 50       19 status => 'CRITICAL',
149             info => sprintf( 'Error for %s: Failed writing to key %s',
150             $description,
151             $key,
152             ),
153             } unless defined $val;
154             }
155              
156             1;
157              
158             __END__