Merge 'normalize_connect_info' into 'trunk'
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Replicated.pm
CommitLineData
2156bbdd 1package DBIx::Class::Storage::DBI::Replicated;
f5d3a5de 2
ecb65397 3BEGIN {
4 use Carp::Clan qw/^DBIx::Class/;
d4daee7b 5
ecb65397 6 ## Modules required for Replication support not required for general DBIC
7 ## use, so we explicitly test for these.
d4daee7b 8
ecb65397 9 my %replication_required = (
c4d78acb 10 'Moose' => '0.90',
62eabf11 11 'MooseX::Types' => '0.16',
bd5da369 12 'namespace::clean' => '0.11',
13 'Hash::Merge' => '0.11'
ecb65397 14 );
d4daee7b 15
ecb65397 16 my @didnt_load;
d4daee7b 17
ecb65397 18 for my $module (keys %replication_required) {
1a58752c 19 eval "use $module $replication_required{$module}";
20 push @didnt_load, "$module $replication_required{$module}"
21 if $@;
ecb65397 22 }
d4daee7b 23
ecb65397 24 croak("@{[ join ', ', @didnt_load ]} are missing and are required for Replication")
d4daee7b 25 if @didnt_load;
ecb65397 26}
27
b2e4d522 28use Moose;
26ab719a 29use DBIx::Class::Storage::DBI;
2bf79155 30use DBIx::Class::Storage::DBI::Replicated::Pool;
26ab719a 31use DBIx::Class::Storage::DBI::Replicated::Balancer;
6a151f58 32use DBIx::Class::Storage::DBI::Replicated::Types qw/BalancerClassNamePart DBICSchema DBICStorageDBI/;
41916570 33use MooseX::Types::Moose qw/ClassName HashRef Object/;
b2e4d522 34use Scalar::Util 'reftype';
b88b85e7 35use Hash::Merge 'merge';
9901aad7 36
37use namespace::clean -except => 'meta';
2bf79155 38
39=head1 NAME
40
ecb65397 41DBIx::Class::Storage::DBI::Replicated - BETA Replicated database support
2bf79155 42
43=head1 SYNOPSIS
44
45The Following example shows how to change an existing $schema to a replicated
46storage type, add some replicated (readonly) databases, and perform reporting
955a6df6 47tasks.
2bf79155 48
3da4f736 49You should set the 'storage_type attribute to a replicated type. You should
d4daee7b 50also define your arguments, such as which balancer you want and any arguments
3da4f736 51that the Pool object should get.
52
64cdad22 53 $schema->storage_type( ['::DBI::Replicated', {balancer=>'::Random'}] );
d4daee7b 54
3da4f736 55Next, you need to add in the Replicants. Basically this is an array of
56arrayrefs, where each arrayref is database connect information. Think of these
57arguments as what you'd pass to the 'normal' $schema->connect method.
d4daee7b 58
64cdad22 59 $schema->storage->connect_replicants(
60 [$dsn1, $user, $pass, \%opts],
61 [$dsn2, $user, $pass, \%opts],
62 [$dsn3, $user, $pass, \%opts],
63 );
d4daee7b 64
3da4f736 65Now, just use the $schema as you normally would. Automatically all reads will
66be delegated to the replicants, while writes to the master.
67
7e38d850 68 $schema->resultset('Source')->search({name=>'etc'});
d4daee7b 69
3da4f736 70You can force a given query to use a particular storage using the search
71attribute 'force_pool'. For example:
d4daee7b 72
7e38d850 73 my $RS = $schema->resultset('Source')->search(undef, {force_pool=>'master'});
3da4f736 74
75Now $RS will force everything (both reads and writes) to use whatever was setup
76as the master storage. 'master' is hardcoded to always point to the Master,
77but you can also use any Replicant name. Please see:
212cc5c2 78L<DBIx::Class::Storage::DBI::Replicated::Pool> and the replicants attribute for more.
3da4f736 79
80Also see transactions and L</execute_reliably> for alternative ways to
81force read traffic to the master. In general, you should wrap your statements
82in a transaction when you are reading and writing to the same tables at the
83same time, since your replicants will often lag a bit behind the master.
212cc5c2 84
85See L<DBIx::Class::Storage::DBI::Replicated::Instructions> for more help and
86walkthroughs.
d4daee7b 87
2bf79155 88=head1 DESCRIPTION
89
7e38d850 90Warning: This class is marked BETA. This has been running a production
ccb3b897 91website using MySQL native replication as its backend and we have some decent
7e38d850 92test coverage but the code hasn't yet been stressed by a variety of databases.
93Individual DB's may have quirks we are not aware of. Please use this in first
94development and pass along your experiences/bug fixes.
2bf79155 95
96This class implements replicated data store for DBI. Currently you can define
97one master and numerous slave database connections. All write-type queries
98(INSERT, UPDATE, DELETE and even LAST_INSERT_ID) are routed to master
99database, all read-type queries (SELECTs) go to the slave database.
100
101Basically, any method request that L<DBIx::Class::Storage::DBI> would normally
bca099a3 102handle gets delegated to one of the two attributes: L</read_handler> or to
103L</write_handler>. Additionally, some methods need to be distributed
2bf79155 104to all existing storages. This way our storage class is a drop in replacement
105for L<DBIx::Class::Storage::DBI>.
106
107Read traffic is spread across the replicants (slaves) occuring to a user
108selected algorithm. The default algorithm is random weighted.
109
bca099a3 110=head1 NOTES
111
112The consistancy betweeen master and replicants is database specific. The Pool
faaba25f 113gives you a method to validate its replicants, removing and replacing them
7e38d850 114when they fail/pass predefined criteria. Please make careful use of the ways
ecb65397 115to force a query to run against Master when needed.
116
117=head1 REQUIREMENTS
118
119Replicated Storage has additional requirements not currently part of L<DBIx::Class>
120
c4d78acb 121 Moose => '0.90',
62eabf11 122 MooseX::Types => '0.16',
123 namespace::clean => '0.11',
124 Hash::Merge => '0.11'
d4daee7b 125
ecb65397 126You will need to install these modules manually via CPAN or make them part of the
127Makefile for your distribution.
2bf79155 128
129=head1 ATTRIBUTES
130
131This class defines the following attributes.
132
2ce6e9a6 133=head2 schema
134
135The underlying L<DBIx::Class::Schema> object this storage is attaching
136
137=cut
138
139has 'schema' => (
140 is=>'rw',
6a151f58 141 isa=>DBICSchema,
2ce6e9a6 142 weak_ref=>1,
143 required=>1,
144);
145
26ab719a 146=head2 pool_type
2bf79155 147
26ab719a 148Contains the classname which will instantiate the L</pool> object. Defaults
149to: L<DBIx::Class::Storage::DBI::Replicated::Pool>.
2bf79155 150
151=cut
152
26ab719a 153has 'pool_type' => (
dcdf7b2c 154 is=>'rw',
41916570 155 isa=>ClassName,
2ce6e9a6 156 default=>'DBIx::Class::Storage::DBI::Replicated::Pool',
64cdad22 157 handles=>{
158 'create_pool' => 'new',
159 },
2bf79155 160);
161
f068a139 162=head2 pool_args
163
164Contains a hashref of initialized information to pass to the Balancer object.
212cc5c2 165See L<DBIx::Class::Storage::DBI::Replicated::Pool> for available arguments.
f068a139 166
167=cut
168
169has 'pool_args' => (
dcdf7b2c 170 is=>'rw',
41916570 171 isa=>HashRef,
64cdad22 172 lazy=>1,
64cdad22 173 default=>sub { {} },
f068a139 174);
175
176
26ab719a 177=head2 balancer_type
2bf79155 178
179The replication pool requires a balance class to provider the methods for
180choose how to spread the query load across each replicant in the pool.
181
182=cut
183
26ab719a 184has 'balancer_type' => (
dcdf7b2c 185 is=>'rw',
9901aad7 186 isa=>BalancerClassNamePart,
2ce6e9a6 187 coerce=>1,
188 required=>1,
189 default=> 'DBIx::Class::Storage::DBI::Replicated::Balancer::First',
64cdad22 190 handles=>{
191 'create_balancer' => 'new',
192 },
2bf79155 193);
194
17b05c13 195=head2 balancer_args
196
197Contains a hashref of initialized information to pass to the Balancer object.
212cc5c2 198See L<DBIx::Class::Storage::DBI::Replicated::Balancer> for available arguments.
17b05c13 199
200=cut
201
202has 'balancer_args' => (
dcdf7b2c 203 is=>'rw',
41916570 204 isa=>HashRef,
64cdad22 205 lazy=>1,
206 required=>1,
207 default=>sub { {} },
17b05c13 208);
209
26ab719a 210=head2 pool
2bf79155 211
26ab719a 212Is a <DBIx::Class::Storage::DBI::Replicated::Pool> or derived class. This is a
213container class for one or more replicated databases.
2bf79155 214
215=cut
216
26ab719a 217has 'pool' => (
64cdad22 218 is=>'ro',
219 isa=>'DBIx::Class::Storage::DBI::Replicated::Pool',
220 lazy_build=>1,
221 handles=>[qw/
6f7344b8 222 connect_replicants
64cdad22 223 replicants
224 has_replicants
225 /],
2bf79155 226);
227
26ab719a 228=head2 balancer
2bf79155 229
26ab719a 230Is a <DBIx::Class::Storage::DBI::Replicated::Balancer> or derived class. This
231is a class that takes a pool (<DBIx::Class::Storage::DBI::Replicated::Pool>)
2bf79155 232
26ab719a 233=cut
2bf79155 234
26ab719a 235has 'balancer' => (
dcdf7b2c 236 is=>'rw',
64cdad22 237 isa=>'DBIx::Class::Storage::DBI::Replicated::Balancer',
238 lazy_build=>1,
239 handles=>[qw/auto_validate_every/],
26ab719a 240);
2bf79155 241
cb6ec758 242=head2 master
243
244The master defines the canonical state for a pool of connected databases. All
245the replicants are expected to match this databases state. Thus, in a classic
246Master / Slaves distributed system, all the slaves are expected to replicate
247the Master's state as quick as possible. This is the only database in the
248pool of databases that is allowed to handle write traffic.
249
250=cut
251
252has 'master' => (
64cdad22 253 is=> 'ro',
6a151f58 254 isa=>DBICStorageDBI,
64cdad22 255 lazy_build=>1,
cb6ec758 256);
257
cb6ec758 258=head1 ATTRIBUTES IMPLEMENTING THE DBIx::Storage::DBI INTERFACE
259
260The following methods are delegated all the methods required for the
261L<DBIx::Class::Storage::DBI> interface.
262
263=head2 read_handler
264
265Defines an object that implements the read side of L<BIx::Class::Storage::DBI>.
266
267=cut
268
269has 'read_handler' => (
64cdad22 270 is=>'rw',
41916570 271 isa=>Object,
64cdad22 272 lazy_build=>1,
273 handles=>[qw/
274 select
275 select_single
276 columns_info_for
6f7344b8 277 /],
cb6ec758 278);
279
cb6ec758 280=head2 write_handler
281
282Defines an object that implements the write side of L<BIx::Class::Storage::DBI>.
283
284=cut
285
286has 'write_handler' => (
64cdad22 287 is=>'ro',
41916570 288 isa=>Object,
64cdad22 289 lazy_build=>1,
6f7344b8 290 handles=>[qw/
64cdad22 291 on_connect_do
6f7344b8 292 on_disconnect_do
64cdad22 293 connect_info
294 throw_exception
295 sql_maker
296 sqlt_type
297 create_ddl_dir
298 deployment_statements
299 datetime_parser
6f7344b8 300 datetime_parser_type
301 build_datetime_parser
64cdad22 302 last_insert_id
303 insert
304 insert_bulk
305 update
306 delete
307 dbh
2ce6e9a6 308 txn_begin
64cdad22 309 txn_do
310 txn_commit
311 txn_rollback
2ce6e9a6 312 txn_scope_guard
64cdad22 313 sth
314 deploy
0180bef9 315 with_deferred_fk_checks
6f7344b8 316 dbh_do
64cdad22 317 reload_row
6f7344b8 318 with_deferred_fk_checks
2ce6e9a6 319 _prep_for_execute
7fb60fb1 320
6f7344b8 321 backup
322 is_datatype_numeric
323 _count_select
324 _subq_count_select
325 _subq_update_delete
326 svp_rollback
327 svp_begin
328 svp_release
64cdad22 329 /],
cb6ec758 330);
331
b2e4d522 332has _master_connect_info_opts =>
333 (is => 'rw', isa => HashRef, default => sub { {} });
334
335=head2 around: connect_info
336
337Preserve master's C<connect_info> options (for merging with replicants.)
dcdf7b2c 338Also set any Replicated related options from connect_info, such as
339C<pool_type>, C<pool_args>, C<balancer_type> and C<balancer_args>.
b2e4d522 340
341=cut
342
343around connect_info => sub {
344 my ($next, $self, $info, @extra) = @_;
345
0ce2d0d5 346 my $wantarray = wantarray;
347
b2e4d522 348 my %opts;
349 for my $arg (@$info) {
350 next unless (reftype($arg)||'') eq 'HASH';
b88b85e7 351 %opts = %{ merge($arg, \%opts) };
b2e4d522 352 }
b2e4d522 353 delete $opts{dsn};
354
dcdf7b2c 355 if (@opts{qw/pool_type pool_args/}) {
356 $self->pool_type(delete $opts{pool_type})
357 if $opts{pool_type};
358
b88b85e7 359 $self->pool_args(
360 merge((delete $opts{pool_args} || {}), $self->pool_args)
361 );
dcdf7b2c 362
67c43863 363 $self->pool($self->_build_pool)
6f7344b8 364 if $self->pool;
dcdf7b2c 365 }
366
367 if (@opts{qw/balancer_type balancer_args/}) {
368 $self->balancer_type(delete $opts{balancer_type})
369 if $opts{balancer_type};
370
b88b85e7 371 $self->balancer_args(
372 merge((delete $opts{balancer_args} || {}), $self->balancer_args)
373 );
dcdf7b2c 374
67c43863 375 $self->balancer($self->_build_balancer)
6f7344b8 376 if $self->balancer;
dcdf7b2c 377 }
378
b2e4d522 379 $self->_master_connect_info_opts(\%opts);
380
0ce2d0d5 381 my (@res, $res);
382 if ($wantarray) {
383 @res = $self->$next($info, @extra);
384 } else {
385 $res = $self->$next($info, @extra);
386 }
387
fd4eb9c2 388 # Make sure master is blessed into the correct class and apply role to it.
389 my $master = $self->master;
390 $master->_determine_driver;
391 Moose::Meta::Class->initialize(ref $master);
392 DBIx::Class::Storage::DBI::Replicated::WithDSN->meta->apply($master);
0ce2d0d5 393
394 $wantarray ? @res : $res;
b2e4d522 395};
396
26ab719a 397=head1 METHODS
2bf79155 398
26ab719a 399This class defines the following methods.
2bf79155 400
c354902c 401=head2 BUILDARGS
2bf79155 402
faaba25f 403L<DBIx::Class::Schema> when instantiating its storage passed itself as the
2ce6e9a6 404first argument. So we need to massage the arguments a bit so that all the
405bits get put into the correct places.
2bf79155 406
407=cut
408
c354902c 409sub BUILDARGS {
410 my ($class, $schema, $storage_type_args, @args) = @_;
d4daee7b 411
c354902c 412 return {
6f7344b8 413 schema=>$schema,
414 %$storage_type_args,
415 @args
c354902c 416 }
417}
2bf79155 418
cb6ec758 419=head2 _build_master
2bf79155 420
cb6ec758 421Lazy builder for the L</master> attribute.
2bf79155 422
423=cut
424
cb6ec758 425sub _build_master {
2ce6e9a6 426 my $self = shift @_;
ee356d00 427 my $master = DBIx::Class::Storage::DBI->new($self->schema);
ee356d00 428 $master
106d5f3b 429}
430
26ab719a 431=head2 _build_pool
2bf79155 432
26ab719a 433Lazy builder for the L</pool> attribute.
2bf79155 434
435=cut
436
26ab719a 437sub _build_pool {
64cdad22 438 my $self = shift @_;
439 $self->create_pool(%{$self->pool_args});
2bf79155 440}
441
26ab719a 442=head2 _build_balancer
2bf79155 443
cb6ec758 444Lazy builder for the L</balancer> attribute. This takes a Pool object so that
445the balancer knows which pool it's balancing.
2bf79155 446
447=cut
448
26ab719a 449sub _build_balancer {
64cdad22 450 my $self = shift @_;
451 $self->create_balancer(
6f7344b8 452 pool=>$self->pool,
64cdad22 453 master=>$self->master,
454 %{$self->balancer_args},
455 );
2bf79155 456}
457
cb6ec758 458=head2 _build_write_handler
2bf79155 459
cb6ec758 460Lazy builder for the L</write_handler> attribute. The default is to set this to
461the L</master>.
50336325 462
463=cut
464
cb6ec758 465sub _build_write_handler {
64cdad22 466 return shift->master;
cb6ec758 467}
50336325 468
cb6ec758 469=head2 _build_read_handler
2bf79155 470
cb6ec758 471Lazy builder for the L</read_handler> attribute. The default is to set this to
472the L</balancer>.
2bf79155 473
474=cut
475
cb6ec758 476sub _build_read_handler {
64cdad22 477 return shift->balancer;
cb6ec758 478}
50336325 479
cb6ec758 480=head2 around: connect_replicants
2bf79155 481
cb6ec758 482All calls to connect_replicants needs to have an existing $schema tacked onto
b2e4d522 483top of the args, since L<DBIx::Storage::DBI> needs it, and any C<connect_info>
484options merged with the master, with replicant opts having higher priority.
955a6df6 485
cb6ec758 486=cut
955a6df6 487
b2e4d522 488around connect_replicants => sub {
489 my ($next, $self, @args) = @_;
490
491 for my $r (@args) {
492 $r = [ $r ] unless reftype $r eq 'ARRAY';
493
1a58752c 494 $self->throw_exception('coderef replicant connect_info not supported')
b2e4d522 495 if ref $r->[0] && reftype $r->[0] eq 'CODE';
496
497# any connect_info options?
498 my $i = 0;
499 $i++ while $i < @$r && (reftype($r->[$i])||'') ne 'HASH';
500
6f7344b8 501# make one if none
b2e4d522 502 $r->[$i] = {} unless $r->[$i];
503
504# merge if two hashes
b88b85e7 505 my @hashes = @$r[$i .. $#{$r}];
506
1a58752c 507 $self->throw_exception('invalid connect_info options')
b88b85e7 508 if (grep { reftype($_) eq 'HASH' } @hashes) != @hashes;
509
1a58752c 510 $self->throw_exception('too many hashrefs in connect_info')
b88b85e7 511 if @hashes > 2;
512
513 my %opts = %{ merge(reverse @hashes) };
514
515# delete them
b2e4d522 516 splice @$r, $i+1, ($#{$r} - $i), ();
517
0bd8e058 518# make sure master/replicants opts don't clash
519 my %master_opts = %{ $self->_master_connect_info_opts };
520 if (exists $opts{dbh_maker}) {
521 delete @master_opts{qw/dsn user password/};
522 }
523 delete $master_opts{dbh_maker};
524
b2e4d522 525# merge with master
0bd8e058 526 %opts = %{ merge(\%opts, \%master_opts) };
b2e4d522 527
528# update
529 $r->[$i] = \%opts;
530 }
531
532 $self->$next($self->schema, @args);
955a6df6 533};
2bf79155 534
2bf79155 535=head2 all_storages
536
537Returns an array of of all the connected storage backends. The first element
538in the returned array is the master, and the remainings are each of the
539replicants.
540
541=cut
542
543sub all_storages {
64cdad22 544 my $self = shift @_;
545 return grep {defined $_ && blessed $_} (
546 $self->master,
6412a592 547 values %{ $self->replicants },
64cdad22 548 );
2bf79155 549}
550
c4d3fae2 551=head2 execute_reliably ($coderef, ?@args)
552
553Given a coderef, saves the current state of the L</read_handler>, forces it to
554use reliable storage (ie sets it to the master), executes a coderef and then
555restores the original state.
556
557Example:
558
64cdad22 559 my $reliably = sub {
560 my $name = shift @_;
561 $schema->resultset('User')->create({name=>$name});
562 my $user_rs = $schema->resultset('User')->find({name=>$name});
563 return $user_rs;
564 };
c4d3fae2 565
64cdad22 566 my $user_rs = $schema->storage->execute_reliably($reliably, 'John');
c4d3fae2 567
568Use this when you must be certain of your database state, such as when you just
569inserted something and need to get a resultset including it, etc.
570
571=cut
572
573sub execute_reliably {
64cdad22 574 my ($self, $coderef, @args) = @_;
d4daee7b 575
64cdad22 576 unless( ref $coderef eq 'CODE') {
577 $self->throw_exception('Second argument must be a coderef');
578 }
d4daee7b 579
64cdad22 580 ##Get copy of master storage
581 my $master = $self->master;
d4daee7b 582
64cdad22 583 ##Get whatever the current read hander is
584 my $current = $self->read_handler;
d4daee7b 585
64cdad22 586 ##Set the read handler to master
587 $self->read_handler($master);
d4daee7b 588
64cdad22 589 ## do whatever the caller needs
590 my @result;
591 my $want_array = wantarray;
d4daee7b 592
64cdad22 593 eval {
594 if($want_array) {
595 @result = $coderef->(@args);
596 } elsif(defined $want_array) {
597 ($result[0]) = ($coderef->(@args));
ed213e85 598 } else {
64cdad22 599 $coderef->(@args);
6f7344b8 600 }
64cdad22 601 };
d4daee7b 602
64cdad22 603 ##Reset to the original state
6f7344b8 604 $self->read_handler($current);
d4daee7b 605
64cdad22 606 ##Exception testing has to come last, otherwise you might leave the
607 ##read_handler set to master.
d4daee7b 608
64cdad22 609 if($@) {
610 $self->throw_exception("coderef returned an error: $@");
611 } else {
612 return $want_array ? @result : $result[0];
613 }
c4d3fae2 614}
615
cb6ec758 616=head2 set_reliable_storage
617
618Sets the current $schema to be 'reliable', that is all queries, both read and
619write are sent to the master
d4daee7b 620
cb6ec758 621=cut
622
623sub set_reliable_storage {
64cdad22 624 my $self = shift @_;
625 my $schema = $self->schema;
626 my $write_handler = $self->schema->storage->write_handler;
d4daee7b 627
64cdad22 628 $schema->storage->read_handler($write_handler);
cb6ec758 629}
630
631=head2 set_balanced_storage
632
633Sets the current $schema to be use the </balancer> for all reads, while all
634writea are sent to the master only
d4daee7b 635
cb6ec758 636=cut
637
638sub set_balanced_storage {
64cdad22 639 my $self = shift @_;
640 my $schema = $self->schema;
bd5da369 641 my $balanced_handler = $self->schema->storage->balancer;
d4daee7b 642
bd5da369 643 $schema->storage->read_handler($balanced_handler);
cb6ec758 644}
2bf79155 645
646=head2 connected
647
648Check that the master and at least one of the replicants is connected.
649
650=cut
651
652sub connected {
64cdad22 653 my $self = shift @_;
654 return
655 $self->master->connected &&
656 $self->pool->connected_replicants;
2bf79155 657}
658
2bf79155 659=head2 ensure_connected
660
661Make sure all the storages are connected.
662
663=cut
664
665sub ensure_connected {
64cdad22 666 my $self = shift @_;
667 foreach my $source ($self->all_storages) {
668 $source->ensure_connected(@_);
669 }
2bf79155 670}
671
2bf79155 672=head2 limit_dialect
673
674Set the limit_dialect for all existing storages
675
676=cut
677
678sub limit_dialect {
64cdad22 679 my $self = shift @_;
680 foreach my $source ($self->all_storages) {
681 $source->limit_dialect(@_);
682 }
3fbe08e3 683 return $self->master->quote_char;
2bf79155 684}
685
2bf79155 686=head2 quote_char
687
688Set the quote_char for all existing storages
689
690=cut
691
692sub quote_char {
64cdad22 693 my $self = shift @_;
694 foreach my $source ($self->all_storages) {
695 $source->quote_char(@_);
696 }
3fbe08e3 697 return $self->master->quote_char;
2bf79155 698}
699
2bf79155 700=head2 name_sep
701
702Set the name_sep for all existing storages
703
704=cut
705
706sub name_sep {
64cdad22 707 my $self = shift @_;
708 foreach my $source ($self->all_storages) {
709 $source->name_sep(@_);
710 }
3fbe08e3 711 return $self->master->name_sep;
2bf79155 712}
713
2bf79155 714=head2 set_schema
715
716Set the schema object for all existing storages
717
718=cut
719
720sub set_schema {
64cdad22 721 my $self = shift @_;
722 foreach my $source ($self->all_storages) {
723 $source->set_schema(@_);
724 }
2bf79155 725}
726
2bf79155 727=head2 debug
728
729set a debug flag across all storages
730
731=cut
732
733sub debug {
64cdad22 734 my $self = shift @_;
3fbe08e3 735 if(@_) {
736 foreach my $source ($self->all_storages) {
737 $source->debug(@_);
6f7344b8 738 }
64cdad22 739 }
3fbe08e3 740 return $self->master->debug;
2bf79155 741}
742
2bf79155 743=head2 debugobj
744
745set a debug object across all storages
746
747=cut
748
749sub debugobj {
64cdad22 750 my $self = shift @_;
3fbe08e3 751 if(@_) {
752 foreach my $source ($self->all_storages) {
753 $source->debugobj(@_);
6f7344b8 754 }
64cdad22 755 }
3fbe08e3 756 return $self->master->debugobj;
2bf79155 757}
758
2bf79155 759=head2 debugfh
760
761set a debugfh object across all storages
762
763=cut
764
765sub debugfh {
64cdad22 766 my $self = shift @_;
3fbe08e3 767 if(@_) {
768 foreach my $source ($self->all_storages) {
769 $source->debugfh(@_);
6f7344b8 770 }
64cdad22 771 }
3fbe08e3 772 return $self->master->debugfh;
2bf79155 773}
774
2bf79155 775=head2 debugcb
776
777set a debug callback across all storages
778
779=cut
780
781sub debugcb {
64cdad22 782 my $self = shift @_;
3fbe08e3 783 if(@_) {
784 foreach my $source ($self->all_storages) {
785 $source->debugcb(@_);
6f7344b8 786 }
64cdad22 787 }
3fbe08e3 788 return $self->master->debugcb;
2bf79155 789}
790
2bf79155 791=head2 disconnect
792
793disconnect everything
794
795=cut
796
797sub disconnect {
64cdad22 798 my $self = shift @_;
799 foreach my $source ($self->all_storages) {
800 $source->disconnect(@_);
801 }
2bf79155 802}
803
b2e4d522 804=head2 cursor_class
805
806set cursor class on all storages, or return master's
807
808=cut
809
810sub cursor_class {
811 my ($self, $cursor_class) = @_;
812
813 if ($cursor_class) {
814 $_->cursor_class($cursor_class) for $self->all_storages;
815 }
816 $self->master->cursor_class;
817}
d4daee7b 818
7e38d850 819=head1 GOTCHAS
820
821Due to the fact that replicants can lag behind a master, you must take care to
822make sure you use one of the methods to force read queries to a master should
823you need realtime data integrity. For example, if you insert a row, and then
824immediately re-read it from the database (say, by doing $row->discard_changes)
825or you insert a row and then immediately build a query that expects that row
826to be an item, you should force the master to handle reads. Otherwise, due to
827the lag, there is no certainty your data will be in the expected state.
828
829For data integrity, all transactions automatically use the master storage for
830all read and write queries. Using a transaction is the preferred and recommended
831method to force the master to handle all read queries.
832
833Otherwise, you can force a single query to use the master with the 'force_pool'
834attribute:
835
836 my $row = $resultset->search(undef, {force_pool=>'master'})->find($pk);
837
838This attribute will safely be ignore by non replicated storages, so you can use
839the same code for both types of systems.
840
841Lastly, you can use the L</execute_reliably> method, which works very much like
842a transaction.
843
844For debugging, you can turn replication on/off with the methods L</set_reliable_storage>
845and L</set_balanced_storage>, however this operates at a global level and is not
846suitable if you have a shared Schema object being used by multiple processes,
847such as on a web application server. You can get around this limitation by
848using the Schema clone method.
849
850 my $new_schema = $schema->clone;
851 $new_schema->set_reliable_storage;
d4daee7b 852
7e38d850 853 ## $new_schema will use only the Master storage for all reads/writes while
854 ## the $schema object will use replicated storage.
855
f5d3a5de 856=head1 AUTHOR
857
64cdad22 858 John Napiorkowski <john.napiorkowski@takkle.com>
f5d3a5de 859
c4d3fae2 860Based on code originated by:
f5d3a5de 861
64cdad22 862 Norbert Csongrádi <bert@cpan.org>
863 Peter Siklósi <einon@einon.hu>
2156bbdd 864
f5d3a5de 865=head1 LICENSE
866
867You may distribute this code under the same terms as Perl itself.
868
869=cut
870
c354902c 871__PACKAGE__->meta->make_immutable;
872
f5d3a5de 8731;