File Coverage

blib/lib/ResourcePool/LoadBalancer.pm
Criterion Covered Total %
statement 135 160 84.3
branch 38 46 82.6
condition 20 24 83.3
subroutine 16 20 80.0
pod 5 16 31.2
total 214 266 80.4


line stmt bran cond sub pod time code
1             #*********************************************************************
2             #*** ResourcePool::LoadBalancer
3             #*** Copyright (c) 2002,2003 by Markus Winand
4             #*** $Id: LoadBalancer.pm,v 1.39 2013-04-16 10:14:44 mws Exp $
5             #*********************************************************************
6              
7             ######
8             # TODO
9             #
10             # -> statistics function
11             # -> DEBUG
12              
13             package ResourcePool::LoadBalancer;
14              
15 3     3   7808 use strict;
  3         6  
  3         124  
16 3     3   18 use vars qw($VERSION @ISA);
  3         7  
  3         153  
17 3     3   18 use ResourcePool::Singleton;
  3         6  
  3         67  
18 3     3   37 use ResourcePool::Command::Execute;
  3         6  
  3         5647  
19              
20             push @ISA, ("ResourcePool::Command::Execute", "ResourcePool::Singleton");
21             $VERSION = "1.0107";
22              
23             sub new($$@) {
24 12     12 1 1759 my $proto = shift;
25 12   33     70 my $class = ref($proto) || $proto;
26 12         22 my $key = shift;
27 12         14 my $self;
28              
29 12         312 $self = $class->SUPER::new($key); # Singleton
30              
31 12 100       70 if (! exists($self->{Policy})) {
32 9         26 $self->{key} = $key;
33 9         20 $self->{PoolArray} = (); # empty pool list
34 9         21 $self->{PoolArraySize} = 0; # empty pool list
35 9         19 $self->{PoolHash} = (); # empty pool hash
36 9         20 $self->{UsedPool} = (); # mapping from plain_resource to
37             # rich pool
38 9         21 $self->{Next} = 0;
39 9         65 my %options = (
40             Policy => "LeastUsage",
41             MaxTry => 6,
42             MaxExecTry => 6,
43             # RoundRobin, LeastUsage, FallBack
44             SleepOnFail => [0,1,2,4,8]
45             );
46              
47 9 50       54 if (scalar(@_) == 1) {
    100          
48 0         0 %options = ((%options), %{$_[0]});
  0         0  
49             } elsif (scalar(@_) > 1) {
50 7         60 %options = ((%options), @_);
51             }
52              
53 9         48 $options{Policy} = uc($options{Policy});
54 9 50 100     78 if ($options{Policy} ne "LEASTUSAGE" &&
      100        
      100        
      66        
55             $options{Policy} ne "ROUNDROBIN" &&
56             $options{Policy} ne "FAILOVER" &&
57             $options{Policy} ne "FAILBACK" &&
58             $options{Policy} ne "FALLBACK") {
59 0         0 $options{Policy} = "LEASTUSAGE";
60             }
61              
62 9 100       36 if (ref($options{SleepOnFail})) {
63 8         30 push (@{$options{SleepOnFail}},
  8         24  
64             ($options{SleepOnFail}->[-1]) x
65 8         10 ($options{MaxTry} - 1 - scalar(@{$options{SleepOnFail}})));
66             } else {
67             # convinience if you want set SleepOnFail to a scalar
68 1         6 $options{SleepOnFail}
69             = [($options{SleepOnFail}) x ($options{MaxTry} - 1)];
70             }
71             # truncate list if it is too long
72 9         22 $#{$options{SleepOnFail}} = $options{MaxTry} - 2;
  9         38  
73              
74              
75 9         24 $self->{Policy} = $options{Policy};
76 9         30 $self->{MaxTry} = $options{MaxTry} - 1;
77 9         22 $self->{MaxExecTry} = $options{MaxExecTry} - 1;
78 9         52 $self->{StatSuspend} = 0;
79 9         21 $self->{StatSuspendAll} = 0;
80 9         15 $self->{SleepOnFail} = [reverse @{$options{SleepOnFail}}];
  9         33  
81              
82 9 100       56 if ($self->{Policy} eq "ROUNDROBIN") {
    100          
    100          
    100          
    50          
83 1         3 $class .= "::RoundRobin";
84             } elsif ( $self->{Policy} eq "LEASTUSAGE") {
85 5         10 $class .= "::LeastUsage";
86             } elsif ( $self->{Policy} eq "FALLBACK") {
87 1         3 $class .= "::FallBack";
88             } elsif ( $self->{Policy} eq "FAILBACK") {
89 1         3 $class .= "::FailBack";
90             } elsif ( $self->{Policy} eq "FAILOVER") {
91 1         2 $class .= "::FailOver";
92             }
93              
94 9         1025 eval "require $class";
95 9         68 bless($self, $class);
96             }
97 12         46 return $self;
98             }
99              
100             sub add_pool($$@) {
101 13     13 1 329 my $self = shift;
102 13         22 my $pool = shift;
103              
104 13 50       252 if (! $self->{PoolHash}->{$pool}) {
105 13         113 my %rich_pool = (
106             pool => $pool,
107             BadCount => 0,
108             SuspendTrigger => 1,
109             SuspendTimeout => 5,
110             Suspended => 0,
111             Weight => 100,
112             @_,
113             UsageCount => 0,
114             StatSuspend => 0,
115             StatSuspendTime => 0
116             );
117 13         20 push @{$self->{PoolArray}}, \%rich_pool;
  13         38  
118 13         43 $self->{PoolHash}->{$pool} = \%rich_pool;
119 13         126 $self->{PoolArraySize}++;
120             }
121             }
122              
123              
124             sub get($) {
125 192     192 1 1827 my ($self) = @_;
126 192         228 my $rec;
127 192         627 my $maxtry = $self->{MaxTry};
128 192         212 my $trylength;
129             my $r_pool;
130              
131 192   100     298 do {
      66        
132 196         382 $trylength = $self->{PoolArraySize} - $self->{StatSuspend};
133 196   100     230 do {
134 202         667 ($rec, $r_pool) = $self->get_once();
135             } while (! $rec && ($trylength-- > 0));
136             } while (! $rec && ($maxtry-- > 0) && ($self->sleepit($maxtry)));
137              
138 192 100       389 if ($rec) {
139 190         628 $self->{UsedPool}->{$rec} = $r_pool;
140             }
141 192         583 return $rec;
142             }
143              
144             sub free($$) {
145 183     183 1 2107 my ($self, $rec) = @_;
146 183 50       499 return unless defined $rec;
147 183         444 my $r_pool = $self->{UsedPool}->{$rec};
148              
149 183 100       426 if ($r_pool) {
150 182         850 $r_pool->{pool}->free($rec);
151 182         493 undef $self->{UsedPool}->{$rec};
152             # if ($self->chk_suspend_no_recover($r_pool)) {
153             # $r_pool->{pool}->downsize();
154             # }
155 182         807 return $self->free_policy($r_pool);
156             } else {
157 1         9 return 0;
158             }
159             }
160              
161             sub free_policy($$) {
162 59     59 0 150 return 1;
163             }
164              
165             sub fail($$) {
166 5     5 1 486 my ($self, $rec) = @_;
167 5         20 my $r_pool = $self->{UsedPool}->{$rec};
168              
169 5 50       13 if (defined $r_pool) {
170 5         18 $r_pool->{pool}->fail($rec);
171 5         10 undef $self->{UsedPool}->{$rec};
172 5 50       11 if (! $self->chk_suspend($r_pool)) {
173 5         17 $self->suspend($r_pool);
174             }
175 5         12 return 1;
176             } else {
177 0         0 return 0;
178             }
179             }
180              
181             sub downsize($) {
182 0     0 0 0 my ($self) = @_;
183 0         0 my $r_pool;
184              
185 0         0 foreach $r_pool (@{$self->{PoolArray}}) {
  0         0  
186 0         0 $r_pool->{pool}->downsize();
187             }
188             }
189              
190             sub info($) {
191 0     0 0 0 my ($self) = @_;
192              
193 0         0 return $self->{key};
194             }
195              
196             sub get_stat_used($) {
197 0     0 0 0 my ($self) = @_;
198 0         0 my $r_pool;
199 0         0 my $used = 0;
200              
201 0         0 foreach $r_pool (@{$self->{PoolArray}}) {
  0         0  
202 0         0 $used += $r_pool->{pool}->get_stat_used();
203             }
204 0         0 return $used;
205             }
206              
207             sub get_stat_free($) {
208 0     0 0 0 my ($self) = @_;
209 0         0 my $r_pool;
210 0         0 my $free = 0;
211              
212 0         0 foreach $r_pool (@{$self->{PoolArray}}) {
  0         0  
213 0         0 $free += $r_pool->{pool}->get_stat_free();
214             }
215 0         0 return $free;
216             }
217             ###
218             # private
219              
220             sub suspend($$) {
221 11     11 0 31 my ($self, $r_pool) = @_;
222            
223 11 100       38 if ($r_pool->{SuspendTimeout} <= 0) {
224 4         5 return;
225             }
226              
227 7 50       15 if (! $self->chk_suspend_no_recover($r_pool)) {
228 7         32 swarn("LoadBalancer(%s): Suspending pool to '%s' for %s seconds\n",
229             $self->{key},
230             $r_pool->{pool}->info(),
231             $r_pool->{SuspendTimeout});
232 7         39 $r_pool->{Suspended} = time + $r_pool->{SuspendTimeout};
233 7         24 $r_pool->{pool}->downsize();
234 7         28 $r_pool->{StatSuspend}++;
235 7         13 $self->{StatSuspend}++;
236 7         164 $self->{StatSuspendAll}++;
237             }
238             }
239              
240             sub chk_suspend($$) {
241 318     318 0 582 my ($self, $r_pool) = @_;
242             # my $r_pool = $self->{PoolHash}->{$pool};
243              
244 318 100       960 if ($self->chk_suspend_no_recover($r_pool)) {
245 64 100       149 if ($r_pool->{Suspended} <= time()) {
246 3         9 $self->{StatSuspend}--;
247 3         11 $r_pool->{StatSuspendTime} += $r_pool->{SuspendTimeout};
248 3         9 $r_pool->{StatSuspendTime} += time() - $r_pool->{Suspended};
249              
250 3         43 $r_pool->{UsageCount} = $self->get_avg_usagecount();
251 3         7 $r_pool->{Suspended} = 0;
252 3         18 swarn("LoadBalancer(%s): Recovering pool to '%s'\n",
253             $self->{key},
254             $r_pool->{pool}->info());
255 3         29 return 0;
256             } else {
257 61         439 return 1;
258             }
259             } else {
260 254         1097 return 0;
261             }
262             }
263              
264             sub chk_suspend_no_recover($$) {
265 331     331 0 470 my ($self, $r_pool) = @_;
266              
267 331         966 return $r_pool->{Suspended};
268             }
269              
270             sub get_avg_usagecount($) {
271 3     3 0 6 my ($self) = @_;
272 3         5 my $r_pool;
273 3         7 my $usage_sum = 0;
274 3         6 my $cnt = 0;
275              
276 3         8 foreach $r_pool (@{$self->{PoolArray}}) {
  3         9  
277 6 100       14 if (! $self->chk_suspend_no_recover($r_pool)) {
278 2         5 $usage_sum += $r_pool->{UsageCount};
279 2         8 $cnt++;
280             }
281             }
282 3 100       11 if ($cnt > 0) {
283 2         11 return $usage_sum / $cnt;
284             } else {
285 1         4 return 0;
286             }
287             }
288              
289             sub sleepit($$) {
290 4     4 0 7 my ($self, $try) = @_;
291 4         5 my ($r_pool);
292              
293 4 100       13 if ($self->{SleepOnFail}->[$try] > 0) {
294 1         3 swarn("ResourcePool::LoadBalancer> sleeping %s seconds...\n",
295             $self->{SleepOnFail}->[$try]);
296 1         1000164 sleep($self->{SleepOnFail}->[$try]);
297             }
298              
299 4         11 foreach $r_pool (@{$self->{PoolArray}}) {
  4         19  
300 6         35 $self->chk_suspend($r_pool);
301             }
302 4         24 return 1;
303             }
304              
305             sub swarn($@) {
306 11     11 0 22 my $fmt = shift;
307 11         181 warn sprintf($fmt, @_);
308             }
309              
310             1;