changed the way args are passed to a storage, should make it easier to use existing...
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Replicated / Balancer.pm
1 package DBIx::Class::Storage::DBI::Replicated::Balancer;
2
3 use Moose;
4
5 =head1 NAME
6
7 DBIx::Class::Storage::DBI::Replicated::Balancer; A Software Load Balancer 
8
9 =head1 SYNOPSIS
10
11 This class is used internally by L<DBIx::Class::Storage::DBI::Replicated>.  You
12 shouldn't need to create instances of this class.
13     
14 =head1 DESCRIPTION
15
16 Given a pool (L<DBIx::Class::Storage::DBI::Replicated::Pool>) of replicated
17 database's (L<DBIx::Class::Storage::DBI::Replicated::Replicant>), defines a
18 method by which query load can be spread out across each replicant in the pool.
19
20 =head1 ATTRIBUTES
21
22 This class defines the following attributes.
23
24 =head2 master
25
26 The L<DBIx::Class::Storage::DBI> object that is the master database all the
27 replicants are trying to follow.  The balancer needs to know it since it's the
28 ultimate fallback.
29
30 =cut
31
32 has 'master' => (
33     is=>'ro',
34     isa=>'DBIx::Class::Storage::DBI',
35     required=>1,
36 );
37
38 =head2 pool
39
40 The L<DBIx::Class::Storage::DBI::Replicated::Pool> object that we are trying to
41 balance.
42
43 =cut
44
45 has 'pool' => (
46     is=>'ro',
47     isa=>'DBIx::Class::Storage::DBI::Replicated::Pool',
48     required=>1,
49 );
50
51 =head2 current_replicant
52
53 Replicant storages (slaves) handle all read only traffic.  The assumption is
54 that your database will become readbound well before it becomes write bound
55 and that being able to spread your read only traffic around to multiple 
56 databases is going to help you to scale traffic.
57
58 This attribute returns the next slave to handle a read request.  Your L</pool>
59 attribute has methods to help you shuffle through all the available replicants
60 via it's balancer object.
61
62 =cut
63
64 has 'current_replicant' => (
65     is=> 'rw',
66     isa=>'DBIx::Class::Storage::DBI',
67     lazy_build=>1,
68     handles=>[qw/
69         select
70         select_single
71         columns_info_for
72     /],
73 );
74
75 =head1 METHODS
76
77 This class defines the following methods.
78
79 =head2 _build_current_replicant
80
81 Lazy builder for the L</current_replicant_storage> attribute.
82
83 =cut
84
85 sub _build_current_replicant {
86     my $self = shift @_;
87     $self->next_storage;
88 }
89
90 =head2 next_storage
91
92 Given a pool object, return the next replicant that will serve queries.  The
93 default behavior is to grap the first replicant it finds but you can write 
94 your own subclasses of L<DBIx::Class::Storage::DBI::Replicated::Balancer> to 
95 support other balance systems.
96
97 This returns from the pool of active replicants.  If there are no active
98 replicants, then you should have it return the master as an ultimate fallback.
99
100 =cut
101
102 sub next_storage {
103         my $self = shift @_;
104         my $next = ($self->pool->active_replicants)[0];
105         return $next ? $next:$self->master;
106 }
107
108 =head2 before: select
109
110 Advice on the select attribute.  Each time we use a replicant
111 we need to change it via the storage pool algorithm.  That way we are spreading
112 the load evenly (hopefully) across existing capacity.
113
114 =cut
115
116 before 'select' => sub {
117     my $self = shift @_;
118     my $next_replicant = $self->next_storage;
119     $self->current_replicant($next_replicant);
120 };
121
122 =head2 before: select_single
123
124 Advice on the select_single attribute.  Each time we use a replicant
125 we need to change it via the storage pool algorithm.  That way we are spreading
126 the load evenly (hopefully) across existing capacity.
127
128 =cut
129
130 before 'select_single' => sub {
131     my $self = shift @_;
132     my $next_replicant = $self->next_storage;
133     $self->current_replicant($next_replicant);
134 };
135
136 =head2 before: columns_info_for
137
138 Advice on the current_replicant_storage attribute.  Each time we use a replicant
139 we need to change it via the storage pool algorithm.  That way we are spreading
140 the load evenly (hopefully) across existing capacity.
141
142 =cut
143
144 before 'columns_info_for' => sub {
145     my $self = shift @_;
146     my $next_replicant = $self->next_storage;
147     $self->current_replicant($next_replicant);
148 };
149
150 =head1 AUTHOR
151
152 John Napiorkowski <john.napiorkowski@takkle.com>
153
154 =head1 LICENSE
155
156 You may distribute this code under the same terms as Perl itself.
157
158 =cut
159
160 1;