Merge 'DBIx-Class-current' into 'replication'
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Replication.pm
CommitLineData
325e3466 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 - 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
82aa2963 24 <...>,
25 { limit_dialect => 'LimitXY' } # If needed, see below
325e3466 26 ] );
27
28=head1 DESCRIPTION
29
30This class implements replicated data store for DBI. Currently you can define one master and numerous slave database
31connections. All write-type queries (INSERT, UPDATE, DELETE and even LAST_INSERT_ID) are routed to master database,
32all read-type queries (SELECTs) go to the slave database.
33
34For every slave database you can define a priority value, which controls data source usage pattern. It uses
35L<DBD::Multi>, so first the lower priority data sources used (if they have the same priority, the are used
36randomized), than if all low priority data sources fail, higher ones tried in order.
37
82aa2963 38=head1 CONFIGURATION
39
40=head2 Limit dialect
41
42If you use LIMIT in your queries (effectively, if you use SQL::Abstract::Limit), do not forget to set up limit_dialect (perldoc SQL::Abstract::Limit) by passing it as an option in the (optional) hash reference to connect_info.
43DBIC can not set it up automatically, since it can not guess DBD::Multi connection types.
44
325e3466 45=cut
46
47sub new {
48 my $proto = shift;
49 my $class = ref( $proto ) || $proto;
dbc6d854 50 my $self = {};
325e3466 51
52 bless( $self, $class );
53
54 $self->write_source( DBIx::Class::Storage::DBI->new );
55 $self->read_source( DBIx::Class::Storage::DBI->new );
56
57 return $self;
58}
59
60sub all_sources {
61 my $self = shift;
62
dbc6d854 63 my @sources = ($self->read_source, $self->write_source);
325e3466 64
65 return wantarray ? @sources : \@sources;
66}
67
68sub connect_info {
69 my( $self, $source_info ) = @_;
70
85c23e63 71 my( $info, $global_options, $options, @dsns );
82aa2963 72
85c23e63 73 $info = [ @$source_info ];
325e3466 74
85c23e63 75 $global_options = ref $info->[-1] eq 'HASH' ? pop( @$info ) : {};
76 if( ref( $options = $info->[0]->[-1] ) eq 'HASH' ) {
77 # Local options present in dsn, merge them with global options
78 map { $global_options->{$_} = $options->{$_} } keys %$options;
79 pop @{$info->[0]};
80 }
81
82 # We need to copy-pass $global_options, since connect_info clears it while processing options
83 $self->write_source->connect_info( [ @{$info->[0]}, { %$global_options } ] );
84
85 @dsns = map { ($_->[3]->{priority} || 10) => $_ } @{$info}[1..@$info-1];
86 $global_options->{dsns} = \@dsns;
87
88 $self->read_source->connect_info( [ 'dbi:Multi:', undef, undef, { %$global_options } ] );
325e3466 89}
90
91sub select {
dbc6d854 92 shift->read_source->select( @_ );
325e3466 93}
94sub select_single {
dbc6d854 95 shift->read_source->select_single( @_ );
325e3466 96}
97sub throw_exception {
dbc6d854 98 shift->read_source->throw_exception( @_ );
325e3466 99}
100sub sql_maker {
dbc6d854 101 shift->read_source->sql_maker( @_ );
325e3466 102}
103sub columns_info_for {
dbc6d854 104 shift->read_source->columns_info_for( @_ );
325e3466 105}
106sub sqlt_type {
dbc6d854 107 shift->read_source->sqlt_type( @_ );
325e3466 108}
109sub create_ddl_dir {
dbc6d854 110 shift->read_source->create_ddl_dir( @_ );
325e3466 111}
112sub deployment_statements {
dbc6d854 113 shift->read_source->deployment_statements( @_ );
325e3466 114}
115sub datetime_parser {
dbc6d854 116 shift->read_source->datetime_parser( @_ );
325e3466 117}
118sub datetime_parser_type {
dbc6d854 119 shift->read_source->datetime_parser_type( @_ );
325e3466 120}
121sub build_datetime_parser {
dbc6d854 122 shift->read_source->build_datetime_parser( @_ );
325e3466 123}
124
125sub limit_dialect {
126 my $self = shift;
dbc6d854 127 $self->$_->limit_dialect( @_ ) for( $self->all_sources );
325e3466 128}
129sub quote_char {
130 my $self = shift;
dbc6d854 131 $self->$_->quote_char( @_ ) for( $self->all_sources );
325e3466 132}
133sub name_sep {
134 my $self = shift;
dbc6d854 135 $self->$_->quote_char( @_ ) for( $self->all_sources );
325e3466 136}
137sub disconnect {
138 my $self = shift;
dbc6d854 139 $self->$_->disconnect( @_ ) for( $self->all_sources );
325e3466 140}
141sub DESTROY {
142 my $self = shift;
143
dbc6d854 144 undef $self->{write_source};
145 undef $self->{read_sources};
325e3466 146}
147
148sub last_insert_id {
dbc6d854 149 shift->write_source->last_insert_id( @_ );
325e3466 150}
151sub insert {
dbc6d854 152 shift->write_source->insert( @_ );
325e3466 153}
154sub update {
dbc6d854 155 shift->write_source->update( @_ );
325e3466 156}
157sub update_all {
dbc6d854 158 shift->write_source->update_all( @_ );
325e3466 159}
160sub delete {
dbc6d854 161 shift->write_source->delete( @_ );
325e3466 162}
163sub delete_all {
dbc6d854 164 shift->write_source->delete_all( @_ );
325e3466 165}
166sub create {
dbc6d854 167 shift->write_source->create( @_ );
325e3466 168}
169sub find_or_create {
dbc6d854 170 shift->write_source->find_or_create( @_ );
325e3466 171}
172sub update_or_create {
dbc6d854 173 shift->write_source->update_or_create( @_ );
325e3466 174}
175sub connected {
dbc6d854 176 shift->write_source->connected( @_ );
325e3466 177}
178sub ensure_connected {
dbc6d854 179 shift->write_source->ensure_connected( @_ );
325e3466 180}
181sub dbh {
dbc6d854 182 shift->write_source->dbh( @_ );
325e3466 183}
184sub txn_begin {
dbc6d854 185 shift->write_source->txn_begin( @_ );
325e3466 186}
187sub txn_commit {
dbc6d854 188 shift->write_source->txn_commit( @_ );
325e3466 189}
190sub txn_rollback {
dbc6d854 191 shift->write_source->txn_rollback( @_ );
325e3466 192}
193sub sth {
dbc6d854 194 shift->write_source->sth( @_ );
325e3466 195}
196sub deploy {
dbc6d854 197 shift->write_source->deploy( @_ );
325e3466 198}
199
200
201sub debugfh { shift->_not_supported( 'debugfh' ) };
202sub debugcb { shift->_not_supported( 'debugcb' ) };
203
204sub _not_supported {
205 my( $self, $method ) = @_;
206
207 die "This Storage does not support $method method.";
208}
209
210=head1 SEE ALSO
211
212L<DBI::Class::Storage::DBI>, L<DBD::Multi>, L<DBI>
213
214=head1 AUTHOR
215
216Norbert Csongrádi <bert@cpan.org>
217
dbc6d854 218Peter Siklósi <einon@einon.hu>
325e3466 219
220=head1 LICENSE
221
222You may distribute this code under the same terms as Perl itself.
223
224=cut
225
2261;