File Coverage

blib/lib/DBIx/Roles/AutoReconnect.pm
Criterion Covered Total %
statement 62 82 75.6
branch 24 58 41.3
condition 10 33 30.3
subroutine 8 8 100.0
pod 0 4 0.0
total 104 185 56.2


line stmt bran cond sub pod time code
1             # $Id: AutoReconnect.pm,v 1.4 2005/12/01 18:09:52 dk Exp $
2              
3             package DBIx::Roles::AutoReconnect;
4              
5 1     1   6 use strict;
  1         1  
  1         34  
6 1     1   5 use DBIx::Roles;
  1         1  
  1         6  
7 1     1   4 use vars qw(%defaults $VERSION);
  1         41  
  1         1008  
8              
9             $VERSION = '1.00';
10              
11             %defaults = (
12             ReconnectTimeout => 60,
13             ReconnectMaxTries => 5,
14             ReconnectFailure => undef,
15             );
16              
17             sub initialize
18             {
19 3     3 0 9 my $self = $_[0];
20 3         14 return [], \%defaults;
21             }
22              
23             sub connect
24             {
25 5     5 0 14 my ( $self, $conninfo, $dsn, $user, $pass, $attr) = @_;
26              
27 5         17 @$conninfo = ( $dsn, $user, $pass, $attr );
28            
29 5         28 my ( $super, $private) = $self-> get_super;
30 5 50       15 return unless $super;
31              
32 5         19 my $dbh = db_connect( $self, $conninfo, $super, $private);
33              
34 5         23 return $dbh;
35             }
36              
37             sub db_connect
38             {
39 5     5 0 22 my ( $self, $conninfo, $ref, @param) = @_;
40              
41 5         9 my $attr = $self-> {attr};
42 5         10 my $tries = 0;
43 5         7 my $downtime = 0;
44 5         8 my $ret;
45 5         7 RETRY: while ( 1) {
46             {
47 9         15 my $context = $self-> context;
  9         28  
48 9         19 eval {
49 9         24 local $conninfo->[3]->{RaiseError} = 1;
50 9         38 $ret = $ref->( $self, @param, @$conninfo);
51             };
52 9 100       50 if ( $@) {
    50          
53             # restore context if calls are restarted
54 4         12 $self-> context( $context);
55             } elsif ( $ret) {
56 5 50 33     31 warn "DBIx::Roles::AutoReconnect: successfully reconnected after $tries tries and $downtime sec downtime\n"
      66        
57             if $tries > 0 and (
58             $conninfo->[3]-> {PrintError} or
59             not (exists $conninfo->[3]->{PrintError}) # DBI defaults
60             );
61 5         17 last RETRY;
62             } else {
63 0         0 $@ = "DBIx::Roles::AutoReconnect: Cannot connect(): no suitable roles found";
64             }
65             }
66 4 50       17 $attr-> {ReconnectFailure}->()
67             if $attr-> {ReconnectFailure};
68 4         6 $tries++;
69 4 50 33     25 if (
70             defined ($attr-> {ReconnectMaxTries}) and
71             $attr-> {ReconnectMaxTries} <= $tries
72             ) {
73 0 0       0 $@ = "DBIx::Roles::AutoReconnect: Tried to connect $attr->{ReconnectMaxTries} time(s), giving up\n"
74             unless $@;
75 0 0       0 if ( $conninfo-> [3]-> {RaiseError}) {
76 0         0 die $@;
77             } else {
78 0 0 0     0 warn $@ if
79             not (exists $conninfo->[3]->{PrintError}) # DBI defaults
80             or $conninfo->[3]->{PrintError};
81 0         0 return undef;
82             }
83             }
84 4 50       11 if ( $attr-> {ReconnectTimeout} > 0) {
85 0 0       0 warn "DBIx::AutoReconnect: sleeping for $attr->{ReconnectTimeout} seconds\n"
86             if $conninfo-> [3]->{PrintError};
87 0         0 sleep $attr-> {ReconnectTimeout};
88 0         0 $downtime += $attr-> {ReconnectTimeout};
89             }
90             }
91              
92 5         14 return $ret;
93             }
94              
95             sub dbi_method
96             {
97 11     11 0 32 my ( $self, $conninfo, $method, @parameters) = @_;
98              
99 11 100 66     68 return $self-> super( $method, @parameters)
100             if $method eq 'connect' or not $self->dbh->{AutoCommit};
101              
102 2         8 my ( $wantarray, @ret) = ( wantarray);
103 2         9 my ( $super, $private) = $self-> get_super;
104 2 50       7 return unless $super;
105              
106 2         6 my $tries = 0;
107 2         3 while ( 1) {
108 4 50 33     130 if (
109             defined ($self->{attr}-> {ReconnectMaxTries}) and
110             $self->{attr}-> {ReconnectMaxTries} <= $tries
111             ) {
112 0 0       0 if ( $conninfo-> [3]-> {RaiseError}) {
113 0         0 die "DBIx::Roles::AutoReconnect: Tried to call '$method' $self->{attr}->{ReconnectMaxTries} time(s), giving up\n";
114             } else {
115 0 0 0     0 warn "DBIx::Roles::AutoReconnect: Tried to call '$method' $self->{attr}->{ReconnectMaxTries} time(s), giving up\n" if
116             not exists ($conninfo->[3]->{PrintError}) # DBI defaults
117             or $conninfo->[3]->{PrintError};
118 0         0 return;
119             }
120             }
121 4         6 $tries++;
122              
123 4 50       14 unless ( $self-> dbh) {
124 0 0       0 $conninfo-> [3]-> {RaiseError} ?
125             croak( "DBIx::Roles::AutoReconnect: not connected" ) :
126             return;
127             }
128             # repeatedly call the roles below until they succeed
129             {
130 4         6 local $self-> object-> {RaiseError} = 1;
  4         14  
131 4         22 my $context = $self-> context;
132 4         7 eval {
133 4 50       8 if ( $wantarray) {
134 0         0 @ret = $super-> ($self, $private, $method, @parameters);
135             } else {
136 4         16 $ret[0] = $super-> ($self, $private, $method, @parameters);
137             }
138             };
139 4 50       82 return wantarray ? @ret : $ret[0]
    100          
140             unless $@;
141             # restore context if calls are restarted
142 2         6 $self-> context( $context);
143             }
144 2 50       11 if ( $self-> dbh-> ping) {
145             # DB is alive, most probably that was not a DBI-related error
146 0 0       0 if ( $conninfo-> [3]-> {RaiseError}) {
147 0         0 die $@;
148             } else {
149 0 0 0     0 warn $@ if
150             not (exists $conninfo->[3]->{PrintError}) # DBI defaults
151             or $conninfo->[3]->{PrintError};
152 0         0 return;
153             }
154             } else {
155             # without disconnect
156 2         18 $self-> dbh( $self-> connect( @$conninfo));
157             }
158             }
159             }
160              
161             sub STORE
162             {
163 17     17   34 my ( $self, $conninfo, $key, $val) = @_;
164 17 100 100     115 if ( $key eq 'ReconnectTimeout' or $key eq 'ReconnectMaxTries') {
    50          
    100          
165 4 50       30 die "Fatal: '$key' must be a positive integer"
166             unless $val =~ /^\d+$/;
167             } elsif ( $key eq 'ReconnectFailure') {
168 0 0 0     0 die "Fatal: '$key' must be either 'undef' or a CODE reference"
      0        
169             if not defined($val) or not ref($val) or ref($val) ne 'CODE';
170             } elsif ( not exists $self->{defaults}->{$key}) {
171             # update $attr for eventual reconnects
172 8         23 $conninfo->[3]->{$key} = $val;
173             }
174              
175            
176 17         47 return $self-> super( $key, $val);
177             }
178              
179             1;
180              
181             __DATA__