1 package DBIx::Class::Storage::DBI::Replication;
6 use DBIx::Class::Storage::DBI;
8 use base qw/Class::Accessor::Fast/;
10 __PACKAGE__->mk_accessors( qw/read_source write_source/ );
14 DBIx::Class::Storage::DBI::Replication - EXPERIMENTAL Replicated database support
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
24 [ $dbh, '','', {priority=>10}], # add in a preexisting database handle
25 [ sub { DBI->connect }, '', '', {priority=>10}], # DBD::Multi will call this coderef for connects
27 { limit_dialect => 'LimitXY' } # If needed, see below
32 Warning: This class is marked EXPERIMENTAL. It works for the authors but does
33 not currently have automated tests so your mileage may vary.
35 This class implements replicated data store for DBI. Currently you can define
36 one master and numerous slave database connections. All write-type queries
37 (INSERT, UPDATE, DELETE and even LAST_INSERT_ID) are routed to master
38 database, all read-type queries (SELECTs) go to the slave database.
40 For every slave database you can define a priority value, which controls data
41 source usage pattern. It uses L<DBD::Multi>, so first the lower priority data
42 sources used (if they have the same priority, the are used randomized), than
43 if all low priority data sources fail, higher ones tried in order.
49 If you use LIMIT in your queries (effectively, if you use
50 SQL::Abstract::Limit), do not forget to set up limit_dialect (perldoc
51 SQL::Abstract::Limit) by passing it as an option in the (optional) hash
52 reference to connect_info. DBIC can not set it up automatically, since it can
53 not guess DBD::Multi connection types.
59 my $class = ref( $proto ) || $proto;
62 bless( $self, $class );
64 $self->write_source( DBIx::Class::Storage::DBI->new );
65 $self->read_source( DBIx::Class::Storage::DBI->new );
73 my @sources = ($self->read_source, $self->write_source);
75 return wantarray ? @sources : \@sources;
79 my( $self, $source_info ) = @_;
81 my( $info, $global_options, $options, @dsns );
83 $info = [ @$source_info ];
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
88 map { $global_options->{$_} = $options->{$_} } keys %$options;
92 # We need to copy-pass $global_options, since connect_info clears it while
94 $self->write_source->connect_info( @{$info->[0]}, { %$global_options } );
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.
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];
106 $global_options->{dsns} = \@dsns;
108 $self->read_source->connect_info( [ 'dbi:Multi:', undef, undef, { %$global_options } ] );
112 shift->read_source->select( @_ );
115 shift->read_source->select_single( @_ );
117 sub throw_exception {
118 shift->read_source->throw_exception( @_ );
121 shift->read_source->sql_maker( @_ );
123 sub columns_info_for {
124 shift->read_source->columns_info_for( @_ );
127 shift->read_source->sqlt_type( @_ );
130 shift->read_source->create_ddl_dir( @_ );
132 sub deployment_statements {
133 shift->read_source->deployment_statements( @_ );
135 sub datetime_parser {
136 shift->read_source->datetime_parser( @_ );
138 sub datetime_parser_type {
139 shift->read_source->datetime_parser_type( @_ );
141 sub build_datetime_parser {
142 shift->read_source->build_datetime_parser( @_ );
145 sub limit_dialect { $_->limit_dialect( @_ ) for( shift->all_sources ) }
146 sub quote_char { $_->quote_char( @_ ) for( shift->all_sources ) }
147 sub name_sep { $_->quote_char( @_ ) for( shift->all_sources ) }
148 sub disconnect { $_->disconnect( @_ ) for( shift->all_sources ) }
149 sub set_schema { $_->set_schema( @_ ) for( shift->all_sources ) }
154 undef $self->{write_source};
155 undef $self->{read_sources};
159 shift->write_source->last_insert_id( @_ );
162 shift->write_source->insert( @_ );
165 shift->write_source->update( @_ );
168 shift->write_source->update_all( @_ );
171 shift->write_source->delete( @_ );
174 shift->write_source->delete_all( @_ );
177 shift->write_source->create( @_ );
180 shift->write_source->find_or_create( @_ );
182 sub update_or_create {
183 shift->write_source->update_or_create( @_ );
186 shift->write_source->connected( @_ );
188 sub ensure_connected {
189 shift->write_source->ensure_connected( @_ );
192 shift->write_source->dbh( @_ );
195 shift->write_source->txn_begin( @_ );
198 shift->write_source->txn_commit( @_ );
201 shift->write_source->txn_rollback( @_ );
204 shift->write_source->sth( @_ );
207 shift->write_source->deploy( @_ );
211 sub debugfh { shift->_not_supported( 'debugfh' ) };
212 sub debugcb { shift->_not_supported( 'debugcb' ) };
215 my( $self, $method ) = @_;
217 die "This Storage does not support $method method.";
222 L<DBI::Class::Storage::DBI>, L<DBD::Multi>, L<DBI>
226 Norbert Csongrádi <bert@cpan.org>
228 Peter Siklósi <einon@einon.hu>
232 You may distribute this code under the same terms as Perl itself.