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