removed some debugging comments, removed transaction from Row->get_from_storage,...
[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' => (
64cdad22 33 is=>'rw',
34 isa=>'Int',
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' => (
64cdad22 47 is=>'ro',
48 isa=>'DBIx::Class::Storage::DBI',
49 required=>1,
106d5f3b 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' => (
64cdad22 60 is=>'ro',
61 isa=>'DBIx::Class::Storage::DBI::Replicated::Pool',
62 required=>1,
cb6ec758 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' => (
64cdad22 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 /],
cb6ec758 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 {
64cdad22 100 my $self = shift @_;
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 {
64cdad22 127 my ($next_storage, $self, @args) = @_;
128 my $now = time;
0c90fabe 129
64cdad22 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 }
17b05c13 137
64cdad22 138 ## Get a replicant, or the master if none
139 my $next = $self->$next_storage(@args);
140 return $next ? $next:$self->master;
17b05c13 141};
26ab719a 142
bbafcf26 143=head2 increment_storage
cb6ec758 144
bbafcf26 145Rolls the Storage to whatever is next in the queue, as defined by the Balancer.
cb6ec758 146
147=cut
148
bbafcf26 149sub increment_storage {
64cdad22 150 my $self = shift @_;
151 my $next_replicant = $self->next_storage;
152 $self->current_replicant($next_replicant);
bbafcf26 153}
154
155=head2 around: select
156
157Advice on the select attribute. Each time we use a replicant
158we need to change it via the storage pool algorithm. That way we are spreading
159the load evenly (hopefully) across existing capacity.
160
161=cut
162
163around 'select' => sub {
164 my ($select, $self, @args) = @_;
165
166 if ($args[-1]->{execute_reliably}) {
167 return $self->master->select(@args);
168 } else {
169 $self->increment_storage;
170 return $self->$select(@args);
171 }
cb6ec758 172};
173
bbafcf26 174=head2 around: select_single
cb6ec758 175
176Advice on the select_single attribute. Each time we use a replicant
177we need to change it via the storage pool algorithm. That way we are spreading
178the load evenly (hopefully) across existing capacity.
179
180=cut
181
bbafcf26 182around 'select_single' => sub {
183 my ($select_single, $self, @args) = @_;
184
185 if ($args[-1]->{execute_reliably}) {
186 return $self->master->select_single(@args);
187 } else {
188 $self->increment_storage;
189 return $self->$select_single(@args);
190 }
cb6ec758 191};
192
106d5f3b 193=head2 before: columns_info_for
cb6ec758 194
195Advice on the current_replicant_storage attribute. Each time we use a replicant
196we need to change it via the storage pool algorithm. That way we are spreading
197the load evenly (hopefully) across existing capacity.
198
199=cut
200
106d5f3b 201before 'columns_info_for' => sub {
64cdad22 202 my $self = shift @_;
bbafcf26 203 $self->increment_storage;
cb6ec758 204};
26ab719a 205
206=head1 AUTHOR
207
208John Napiorkowski <john.napiorkowski@takkle.com>
209
210=head1 LICENSE
211
212You may distribute this code under the same terms as Perl itself.
213
214=cut
215
cb6ec758 2161;