minor formatting updates and typos fixes to the schema role tests
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Replicated.pm
CommitLineData
2156bbdd 1package DBIx::Class::Storage::DBI::Replicated;
f5d3a5de 2
3use strict;
4use warnings;
5
6use DBIx::Class::Storage::DBI;
7use DBD::Multi;
2156bbdd 8
f5d3a5de 9use base qw/Class::Accessor::Fast/;
10
11__PACKAGE__->mk_accessors( qw/read_source write_source/ );
12
13=head1 NAME
14
2156bbdd 15DBIx::Class::Storage::DBI::Replicated - ALPHA Replicated database support
f5d3a5de 16
17=head1 SYNOPSIS
18
2156bbdd 19The Following example shows how to change an existing $schema to a replicated
20storage type and update it's connection information to contain a master DSN and
21an array of slaves.
22
23 ## Change storage_type in your schema class
24 $schema->storage_type( '::DBI::Replicated' );
25
26 ## Set your connection.
27 $schema->connect(
28 $dsn, $user, $password, {
29 AutoCommit => 1,
30 ## Other standard DBI connection or DBD custom attributes added as
31 ## usual. Additionally, we have two custom attributes for defining
32 ## slave information and controlling how the underlying DBD::Multi
33 slaves_connect_info => [
34 ## Define each slave like a 'normal' DBI connection, but you add
35 ## in a DBD::Multi custom attribute to define how the slave is
36 ## prioritized. Please see DBD::Multi for more.
37 [$slave1dsn, $user, $password, {%slave1opts, priority=>10}],
38 [$slave2dsn, $user, $password, {%slave2opts, priority=>10}],
39 [$slave3dsn, $user, $password, {%slave3opts, priority=>20}],
40 ## add in a preexisting database handle
41 [$dbh, '','', {priority=>30}],
42 ## DBD::Multi will call this coderef for connects
43 [sub { DBI->connect(< DSN info >) }, '', '', {priority=>40}],
44 ## If the last item is hashref, we use that for DBD::Multi's
45 ## configuration information. Again, see DBD::Multi for more.
46 {timeout=>25, failed_max=>2},
47 ],
48 },
49 );
50
51 ## Now, just use the schema as normal
52 $schema->resultset('Table')->find(< unique >); ## Reads will use slaves
53 $schema->resultset('Table')->create(\%info); ## Writes will use master
f5d3a5de 54
55=head1 DESCRIPTION
56
2156bbdd 57Warning: This class is marked ALPHA. We are using this in development and have
58some basic test coverage but the code hasn't yet been stressed by a variety
59of databases. Individual DB's may have quirks we are not aware of. Please
60use this in development and pass along your experiences/bug fixes.
f5d3a5de 61
0aff97c2 62This class implements replicated data store for DBI. Currently you can define
63one master and numerous slave database connections. All write-type queries
64(INSERT, UPDATE, DELETE and even LAST_INSERT_ID) are routed to master
65database, all read-type queries (SELECTs) go to the slave database.
f5d3a5de 66
0aff97c2 67For every slave database you can define a priority value, which controls data
68source usage pattern. It uses L<DBD::Multi>, so first the lower priority data
69sources used (if they have the same priority, the are used randomized), than
70if all low priority data sources fail, higher ones tried in order.
f5d3a5de 71
72=head1 CONFIGURATION
73
2156bbdd 74Please see L<DBD::Multi> for most configuration information.
f5d3a5de 75
76=cut
77
78sub new {
79 my $proto = shift;
80 my $class = ref( $proto ) || $proto;
81 my $self = {};
82
83 bless( $self, $class );
84
85 $self->write_source( DBIx::Class::Storage::DBI->new );
86 $self->read_source( DBIx::Class::Storage::DBI->new );
87
88 return $self;
89}
90
91sub all_sources {
92 my $self = shift;
93
94 my @sources = ($self->read_source, $self->write_source);
95
96 return wantarray ? @sources : \@sources;
97}
98
2156bbdd 99sub _connect_info {
100 my $self = shift;
101 my $master = $self->write_source->_connect_info;
102 $master->[-1]->{slave_connect_info} = $self->read_source->_connect_info;
103 return $master;
104}
f5d3a5de 105
2156bbdd 106sub connect_info {
107 my ($self, $source_info) = @_;
f5d3a5de 108
2156bbdd 109 ## if there is no $source_info, treat this sub like an accessor
110 return $self->_connect_info
111 if !$source_info;
112
113 ## Alright, let's conect the master
114 $self->write_source->connect_info($source_info);
115
116 ## Now, build and then connect the Slaves
117 my @slaves_connect_info = @{$source_info->[-1]->{slaves_connect_info}};
118 my $dbd_multi_config = ref $slaves_connect_info[-1] eq 'HASH'
119 ? pop @slaves_connect_info : {};
120
121 ## We need to do this since SQL::Abstract::Limit can't guess what DBD::Multi is
122 $dbd_multi_config->{limit_dialect} = $self->write_source->sql_maker->limit_dialect
123 unless defined $dbd_multi_config->{limit_dialect};
124
125 @slaves_connect_info = map {
0f83441a 126 ## if the first element in the arrayhash is a ref, make that the value
127 my $db = ref $_->[0] ? $_->[0] : $_;
2156bbdd 128 my $priority = $_->[-1]->{priority} || 10; ## default priority is 10
129 $priority => $db;
130 } @slaves_connect_info;
0f83441a 131
2156bbdd 132 $self->read_source->connect_info([
133 'dbi:Multi:', undef, undef, {
134 dsns => [@slaves_connect_info],
135 %$dbd_multi_config,
136 },
137 ]);
138
139 ## Return the formated connection information
140 return $self->_connect_info;
f5d3a5de 141}
142
143sub select {
144 shift->read_source->select( @_ );
145}
146sub select_single {
147 shift->read_source->select_single( @_ );
148}
149sub throw_exception {
150 shift->read_source->throw_exception( @_ );
151}
152sub sql_maker {
153 shift->read_source->sql_maker( @_ );
154}
155sub columns_info_for {
156 shift->read_source->columns_info_for( @_ );
157}
158sub sqlt_type {
159 shift->read_source->sqlt_type( @_ );
160}
161sub create_ddl_dir {
162 shift->read_source->create_ddl_dir( @_ );
163}
164sub deployment_statements {
165 shift->read_source->deployment_statements( @_ );
166}
167sub datetime_parser {
168 shift->read_source->datetime_parser( @_ );
169}
170sub datetime_parser_type {
171 shift->read_source->datetime_parser_type( @_ );
172}
173sub build_datetime_parser {
174 shift->read_source->build_datetime_parser( @_ );
175}
176
9b21c682 177sub limit_dialect { $_->limit_dialect( @_ ) for( shift->all_sources ) }
178sub quote_char { $_->quote_char( @_ ) for( shift->all_sources ) }
179sub name_sep { $_->quote_char( @_ ) for( shift->all_sources ) }
180sub disconnect { $_->disconnect( @_ ) for( shift->all_sources ) }
181sub set_schema { $_->set_schema( @_ ) for( shift->all_sources ) }
182
f5d3a5de 183sub DESTROY {
184 my $self = shift;
185
186 undef $self->{write_source};
187 undef $self->{read_sources};
188}
189
190sub last_insert_id {
191 shift->write_source->last_insert_id( @_ );
192}
193sub insert {
194 shift->write_source->insert( @_ );
195}
196sub update {
197 shift->write_source->update( @_ );
198}
199sub update_all {
200 shift->write_source->update_all( @_ );
201}
202sub delete {
203 shift->write_source->delete( @_ );
204}
205sub delete_all {
206 shift->write_source->delete_all( @_ );
207}
208sub create {
209 shift->write_source->create( @_ );
210}
211sub find_or_create {
212 shift->write_source->find_or_create( @_ );
213}
214sub update_or_create {
215 shift->write_source->update_or_create( @_ );
216}
217sub connected {
218 shift->write_source->connected( @_ );
219}
220sub ensure_connected {
221 shift->write_source->ensure_connected( @_ );
222}
223sub dbh {
224 shift->write_source->dbh( @_ );
225}
2156bbdd 226sub txn_do {
227 shift->write_source->txn_do( @_ );
f5d3a5de 228}
229sub txn_commit {
230 shift->write_source->txn_commit( @_ );
231}
232sub txn_rollback {
233 shift->write_source->txn_rollback( @_ );
234}
235sub sth {
236 shift->write_source->sth( @_ );
237}
238sub deploy {
239 shift->write_source->deploy( @_ );
240}
2156bbdd 241sub _prep_for_execute {
242 shift->write_source->_prep_for_execute(@_);
243}
f5d3a5de 244
2156bbdd 245sub debugobj {
246 shift->write_source->debugobj(@_);
247}
248sub debug {
249 shift->write_source->debug(@_);
250}
f5d3a5de 251
252sub debugfh { shift->_not_supported( 'debugfh' ) };
253sub debugcb { shift->_not_supported( 'debugcb' ) };
254
255sub _not_supported {
256 my( $self, $method ) = @_;
257
258 die "This Storage does not support $method method.";
259}
260
261=head1 SEE ALSO
262
263L<DBI::Class::Storage::DBI>, L<DBD::Multi>, L<DBI>
264
265=head1 AUTHOR
266
267Norbert Csongrádi <bert@cpan.org>
268
269Peter Siklósi <einon@einon.hu>
270
2156bbdd 271John Napiorkowski <john.napiorkowski@takkle.com>
272
f5d3a5de 273=head1 LICENSE
274
275You may distribute this code under the same terms as Perl itself.
276
277=cut
278
2791;