renamed replication storage to replicated (since it's not really doing any replicatio...
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Replicated.pm
CommitLineData
f5d3a5de 1package DBIx::Class::Storage::DBI::Replication;
2
3use strict;
4use warnings;
5
6use DBIx::Class::Storage::DBI;
7use DBD::Multi;
8use base qw/Class::Accessor::Fast/;
9
10__PACKAGE__->mk_accessors( qw/read_source write_source/ );
11
12=head1 NAME
13
14DBIx::Class::Storage::DBI::Replication - EXPERIMENTAL Replicated database support
15
16=head1 SYNOPSIS
17
18 # change storage_type in your schema class
19 $schema->storage_type( '::DBI::Replication' );
20 $schema->connect_info( [
21 [ "dbi:mysql:database=test;hostname=master", "username", "password", { AutoCommit => 1 } ], # master
22 [ "dbi:mysql:database=test;hostname=slave1", "username", "password", { priority => 10 } ], # slave1
23 [ "dbi:mysql:database=test;hostname=slave2", "username", "password", { priority => 10 } ], # slave2
0f83441a 24 [ $dbh, '','', {priority=>10}], # add in a preexisting database handle
25 [ sub { DBI->connect }, '', '', {priority=>10}], # DBD::Multi will call this coderef for connects
f5d3a5de 26 <...>,
27 { limit_dialect => 'LimitXY' } # If needed, see below
28 ] );
29
30=head1 DESCRIPTION
31
32Warning: This class is marked EXPERIMENTAL. It works for the authors but does
33not currently have automated tests so your mileage may vary.
34
0aff97c2 35This class implements replicated data store for DBI. Currently you can define
36one master and numerous slave database connections. All write-type queries
37(INSERT, UPDATE, DELETE and even LAST_INSERT_ID) are routed to master
38database, all read-type queries (SELECTs) go to the slave database.
f5d3a5de 39
0aff97c2 40For every slave database you can define a priority value, which controls data
41source usage pattern. It uses L<DBD::Multi>, so first the lower priority data
42sources used (if they have the same priority, the are used randomized), than
43if all low priority data sources fail, higher ones tried in order.
f5d3a5de 44
45=head1 CONFIGURATION
46
47=head2 Limit dialect
48
0aff97c2 49If you use LIMIT in your queries (effectively, if you use
50SQL::Abstract::Limit), do not forget to set up limit_dialect (perldoc
51SQL::Abstract::Limit) by passing it as an option in the (optional) hash
52reference to connect_info. DBIC can not set it up automatically, since it can
53not guess DBD::Multi connection types.
f5d3a5de 54
55=cut
56
57sub new {
58 my $proto = shift;
59 my $class = ref( $proto ) || $proto;
60 my $self = {};
61
62 bless( $self, $class );
63
64 $self->write_source( DBIx::Class::Storage::DBI->new );
65 $self->read_source( DBIx::Class::Storage::DBI->new );
66
67 return $self;
68}
69
70sub all_sources {
71 my $self = shift;
72
73 my @sources = ($self->read_source, $self->write_source);
74
75 return wantarray ? @sources : \@sources;
76}
77
78sub connect_info {
79 my( $self, $source_info ) = @_;
80
81 my( $info, $global_options, $options, @dsns );
82
83 $info = [ @$source_info ];
84
85 $global_options = ref $info->[-1] eq 'HASH' ? pop( @$info ) : {};
86 if( ref( $options = $info->[0]->[-1] ) eq 'HASH' ) {
87 # Local options present in dsn, merge them with global options
0f83441a 88 map { $global_options->{$_} = $options->{$_} } keys %$options;
89 pop @{$info->[0]};
f5d3a5de 90 }
91
0aff97c2 92 # We need to copy-pass $global_options, since connect_info clears it while
93 # processing options
9ac2605b 94 $self->write_source->connect_info( @{$info->[0]}, { %$global_options } );
f5d3a5de 95
0f83441a 96 ## allow either a DSN string or an already connect $dbh. Just remember if
97 ## you use the $dbh option then DBD::Multi has no idea how to reconnect in
98 ## the event of a failure.
99
100 @dsns = map {
101 ## if the first element in the arrayhash is a ref, make that the value
102 my $db = ref $_->[0] ? $_->[0] : $_;
103 ($_->[3]->{priority} || 10) => $db;
104 } @{$info->[0]}[1..@{$info->[0]}-1];
105
f5d3a5de 106 $global_options->{dsns} = \@dsns;
107
108 $self->read_source->connect_info( [ 'dbi:Multi:', undef, undef, { %$global_options } ] );
109}
110
111sub select {
112 shift->read_source->select( @_ );
113}
114sub select_single {
115 shift->read_source->select_single( @_ );
116}
117sub throw_exception {
118 shift->read_source->throw_exception( @_ );
119}
120sub sql_maker {
121 shift->read_source->sql_maker( @_ );
122}
123sub columns_info_for {
124 shift->read_source->columns_info_for( @_ );
125}
126sub sqlt_type {
127 shift->read_source->sqlt_type( @_ );
128}
129sub create_ddl_dir {
130 shift->read_source->create_ddl_dir( @_ );
131}
132sub deployment_statements {
133 shift->read_source->deployment_statements( @_ );
134}
135sub datetime_parser {
136 shift->read_source->datetime_parser( @_ );
137}
138sub datetime_parser_type {
139 shift->read_source->datetime_parser_type( @_ );
140}
141sub build_datetime_parser {
142 shift->read_source->build_datetime_parser( @_ );
143}
144
9b21c682 145sub limit_dialect { $_->limit_dialect( @_ ) for( shift->all_sources ) }
146sub quote_char { $_->quote_char( @_ ) for( shift->all_sources ) }
147sub name_sep { $_->quote_char( @_ ) for( shift->all_sources ) }
148sub disconnect { $_->disconnect( @_ ) for( shift->all_sources ) }
149sub set_schema { $_->set_schema( @_ ) for( shift->all_sources ) }
150
f5d3a5de 151sub DESTROY {
152 my $self = shift;
153
154 undef $self->{write_source};
155 undef $self->{read_sources};
156}
157
158sub last_insert_id {
159 shift->write_source->last_insert_id( @_ );
160}
161sub insert {
162 shift->write_source->insert( @_ );
163}
164sub update {
165 shift->write_source->update( @_ );
166}
167sub update_all {
168 shift->write_source->update_all( @_ );
169}
170sub delete {
171 shift->write_source->delete( @_ );
172}
173sub delete_all {
174 shift->write_source->delete_all( @_ );
175}
176sub create {
177 shift->write_source->create( @_ );
178}
179sub find_or_create {
180 shift->write_source->find_or_create( @_ );
181}
182sub update_or_create {
183 shift->write_source->update_or_create( @_ );
184}
185sub connected {
186 shift->write_source->connected( @_ );
187}
188sub ensure_connected {
189 shift->write_source->ensure_connected( @_ );
190}
191sub dbh {
192 shift->write_source->dbh( @_ );
193}
194sub txn_begin {
195 shift->write_source->txn_begin( @_ );
196}
197sub txn_commit {
198 shift->write_source->txn_commit( @_ );
199}
200sub txn_rollback {
201 shift->write_source->txn_rollback( @_ );
202}
203sub sth {
204 shift->write_source->sth( @_ );
205}
206sub deploy {
207 shift->write_source->deploy( @_ );
208}
209
210
211sub debugfh { shift->_not_supported( 'debugfh' ) };
212sub debugcb { shift->_not_supported( 'debugcb' ) };
213
214sub _not_supported {
215 my( $self, $method ) = @_;
216
217 die "This Storage does not support $method method.";
218}
219
220=head1 SEE ALSO
221
222L<DBI::Class::Storage::DBI>, L<DBD::Multi>, L<DBI>
223
224=head1 AUTHOR
225
226Norbert Csongrádi <bert@cpan.org>
227
228Peter Siklósi <einon@einon.hu>
229
230=head1 LICENSE
231
232You may distribute this code under the same terms as Perl itself.
233
234=cut
235
2361;