changed the balancer to a role, created a new class to define the default balancer...
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Replicated / Balancer.pm
CommitLineData
26ab719a 1package DBIx::Class::Storage::DBI::Replicated::Balancer;
2
17b05c13 3use Moose::Role;
4requires 'next_storage';
26ab719a 5
6=head1 NAME
7
8DBIx::Class::Storage::DBI::Replicated::Balancer; A Software Load Balancer
9
10=head1 SYNOPSIS
11
17b05c13 12This role is used internally by L<DBIx::Class::Storage::DBI::Replicated>.
26ab719a 13
14=head1 DESCRIPTION
15
16Given a pool (L<DBIx::Class::Storage::DBI::Replicated::Pool>) of replicated
17database's (L<DBIx::Class::Storage::DBI::Replicated::Replicant>), defines a
18method by which query load can be spread out across each replicant in the pool.
19
20=head1 ATTRIBUTES
21
22This class defines the following attributes.
23
7edf5f1c 24=head2 auto_validate_every ($seconds)
25
26If auto_validate has some sort of value, run the L<validate_replicants> every
27$seconds. Be careful with this, because if you set it to 0 you will end up
28validating every query.
29
30=cut
31
32has 'auto_validate_every' => (
33 is=>'rw',
34 isa=>'Int',
17b05c13 35 predicate=>'has_auto_validate_every',
7edf5f1c 36);
37
106d5f3b 38=head2 master
39
40The L<DBIx::Class::Storage::DBI> object that is the master database all the
41replicants are trying to follow. The balancer needs to know it since it's the
42ultimate fallback.
43
44=cut
45
46has 'master' => (
47 is=>'ro',
48 isa=>'DBIx::Class::Storage::DBI',
49 required=>1,
50);
51
cb6ec758 52=head2 pool
53
54The L<DBIx::Class::Storage::DBI::Replicated::Pool> object that we are trying to
55balance.
56
57=cut
58
59has 'pool' => (
60 is=>'ro',
61 isa=>'DBIx::Class::Storage::DBI::Replicated::Pool',
62 required=>1,
63);
64
65=head2 current_replicant
66
67Replicant storages (slaves) handle all read only traffic. The assumption is
68that your database will become readbound well before it becomes write bound
69and that being able to spread your read only traffic around to multiple
70databases is going to help you to scale traffic.
71
72This attribute returns the next slave to handle a read request. Your L</pool>
73attribute has methods to help you shuffle through all the available replicants
74via it's balancer object.
75
76=cut
77
78has 'current_replicant' => (
79 is=> 'rw',
80 isa=>'DBIx::Class::Storage::DBI',
81 lazy_build=>1,
82 handles=>[qw/
83 select
84 select_single
85 columns_info_for
86 /],
87);
88
26ab719a 89=head1 METHODS
90
91This class defines the following methods.
92
cb6ec758 93=head2 _build_current_replicant
94
95Lazy builder for the L</current_replicant_storage> attribute.
96
97=cut
98
99sub _build_current_replicant {
100 my $self = shift @_;
106d5f3b 101 $self->next_storage;
cb6ec758 102}
103
104=head2 next_storage
26ab719a 105
17b05c13 106This method should be defined in the class which consumes this role.
107
26ab719a 108Given a pool object, return the next replicant that will serve queries. The
cb6ec758 109default behavior is to grap the first replicant it finds but you can write
110your own subclasses of L<DBIx::Class::Storage::DBI::Replicated::Balancer> to
111support other balance systems.
26ab719a 112
106d5f3b 113This returns from the pool of active replicants. If there are no active
114replicants, then you should have it return the master as an ultimate fallback.
115
17b05c13 116=head2 around: next_storage
117
118Advice on next storage to add the autovalidation. We have this broken out so
119that it's easier to break out the auto validation into a role.
120
121This also returns the master in the case that none of the replicants are active
122or just just forgot to create them :)
7edf5f1c 123
26ab719a 124=cut
125
17b05c13 126around 'next_storage' => sub {
127 my ($next_storage, $self, @args) = @_;
128 my $now = time;
7edf5f1c 129
17b05c13 130 ## Do we need to validate the replicants?
131 if(
132 $self->has_auto_validate_every &&
133 ($self->auto_validate_every + $self->pool->last_validated) <= $now
134 ) {
135 $self->pool->validate_replicants;
136 }
137
138 ## Get a replicant, or the master if none
139 my $next = $self->$next_storage(@args);
140 return $next ? $next:$self->master;
141};
26ab719a 142
106d5f3b 143=head2 before: select
cb6ec758 144
145Advice on the select attribute. Each time we use a replicant
146we need to change it via the storage pool algorithm. That way we are spreading
147the load evenly (hopefully) across existing capacity.
148
149=cut
150
106d5f3b 151before 'select' => sub {
cb6ec758 152 my $self = shift @_;
153 my $next_replicant = $self->next_storage;
154 $self->current_replicant($next_replicant);
155};
156
106d5f3b 157=head2 before: select_single
cb6ec758 158
159Advice on the select_single attribute. Each time we use a replicant
160we need to change it via the storage pool algorithm. That way we are spreading
161the load evenly (hopefully) across existing capacity.
162
163=cut
164
106d5f3b 165before 'select_single' => sub {
cb6ec758 166 my $self = shift @_;
167 my $next_replicant = $self->next_storage;
168 $self->current_replicant($next_replicant);
169};
170
106d5f3b 171=head2 before: columns_info_for
cb6ec758 172
173Advice on the current_replicant_storage attribute. Each time we use a replicant
174we need to change it via the storage pool algorithm. That way we are spreading
175the load evenly (hopefully) across existing capacity.
176
177=cut
178
106d5f3b 179before 'columns_info_for' => sub {
cb6ec758 180 my $self = shift @_;
181 my $next_replicant = $self->next_storage;
182 $self->current_replicant($next_replicant);
183};
26ab719a 184
185=head1 AUTHOR
186
187John Napiorkowski <john.napiorkowski@takkle.com>
188
189=head1 LICENSE
190
191You may distribute this code under the same terms as Perl itself.
192
193=cut
194
cb6ec758 1951;