some documention updates and changed the way we find paths for the sqlite dbfiles...
[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/;
5
6 ## Modules required for Replication support not required for general DBIC
7 ## use, so we explicitly test for these.
8
9 my %replication_required = (
bd5da369 10 'Moose' => '0.77',
11 'MooseX::AttributeHelpers' => '0.12',
12 'MooseX::Types' => '0.10',
13 'namespace::clean' => '0.11',
14 'Hash::Merge' => '0.11'
ecb65397 15 );
16
17 my @didnt_load;
18
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 }
24
25 croak("@{[ join ', ', @didnt_load ]} are missing and are required for Replication")
26 if @didnt_load;
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;
9901aad7 33use DBIx::Class::Storage::DBI::Replicated::Types 'BalancerClassNamePart';
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
52also defined you arguments, such as which balancer you want and any arguments
53that the Pool object should get.
54
64cdad22 55 $schema->storage_type( ['::DBI::Replicated', {balancer=>'::Random'}] );
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.
64cdad22 60
61 $schema->storage->connect_replicants(
62 [$dsn1, $user, $pass, \%opts],
63 [$dsn2, $user, $pass, \%opts],
64 [$dsn3, $user, $pass, \%opts],
65 );
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'});
71
3da4f736 72You can force a given query to use a particular storage using the search
73attribute 'force_pool'. For example:
7e38d850 74
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:
80L<DBIx::Class::Storage::Replicated::Pool> and the replicants attribute for more.
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.
7e38d850 86
2bf79155 87=head1 DESCRIPTION
88
7e38d850 89Warning: This class is marked BETA. This has been running a production
ccb3b897 90website using MySQL native replication as its backend and we have some decent
7e38d850 91test coverage but the code hasn't yet been stressed by a variety of databases.
92Individual DB's may have quirks we are not aware of. Please use this in first
93development and pass along your experiences/bug fixes.
2bf79155 94
95This class implements replicated data store for DBI. Currently you can define
96one master and numerous slave database connections. All write-type queries
97(INSERT, UPDATE, DELETE and even LAST_INSERT_ID) are routed to master
98database, all read-type queries (SELECTs) go to the slave database.
99
100Basically, any method request that L<DBIx::Class::Storage::DBI> would normally
bca099a3 101handle gets delegated to one of the two attributes: L</read_handler> or to
102L</write_handler>. Additionally, some methods need to be distributed
2bf79155 103to all existing storages. This way our storage class is a drop in replacement
104for L<DBIx::Class::Storage::DBI>.
105
106Read traffic is spread across the replicants (slaves) occuring to a user
107selected algorithm. The default algorithm is random weighted.
108
bca099a3 109=head1 NOTES
110
111The consistancy betweeen master and replicants is database specific. The Pool
112gives you a method to validate it's replicants, removing and replacing them
7e38d850 113when they fail/pass predefined criteria. Please make careful use of the ways
ecb65397 114to force a query to run against Master when needed.
115
116=head1 REQUIREMENTS
117
118Replicated Storage has additional requirements not currently part of L<DBIx::Class>
119
9901aad7 120 Moose => 0.77
ecb65397 121 MooseX::AttributeHelpers => 0.12
9901aad7 122 MooseX::Types => 0.10
123 namespace::clean => 0.11
b88b85e7 124 Hash::Merge => 0.11
ecb65397 125
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',
141 isa=>'DBIx::Class::Schema',
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.
165See L<DBIx::Class::Storage::Replicated::Pool> for available arguments.
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.
f068a139 198See L<DBIx::Class::Storage::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/
222 connect_replicants
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',
254 isa=>'DBIx::Class::Storage::DBI',
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
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,
64cdad22 290 handles=>[qw/
291 on_connect_do
292 on_disconnect_do
293 connect_info
294 throw_exception
295 sql_maker
296 sqlt_type
297 create_ddl_dir
298 deployment_statements
299 datetime_parser
7fb60fb1 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
7fb60fb1 316 dbh_do
64cdad22 317 reload_row
7fb60fb1 318 with_deferred_fk_checks
2ce6e9a6 319 _prep_for_execute
7fb60fb1 320
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)
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)
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
cb6ec758 403L<DBIx::Class::Schema> when instantiating it's 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) = @_;
411
412 return {
413 schema=>$schema,
414 %$storage_type_args,
415 @args
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(
452 pool=>$self->pool,
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
494 croak "coderef replicant connect_info not supported"
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
501# make one if none
502 $r->[$i] = {} unless $r->[$i];
503
504# merge if two hashes
b88b85e7 505 my @hashes = @$r[$i .. $#{$r}];
506
507 croak "invalid connect_info options"
508 if (grep { reftype($_) eq 'HASH' } @hashes) != @hashes;
509
510 croak "too many hashrefs in connect_info"
511 if @hashes > 2;
512
513 my %opts = %{ merge(reverse @hashes) };
514
515# delete them
b2e4d522 516 splice @$r, $i+1, ($#{$r} - $i), ();
517
518# merge with master
b88b85e7 519 %opts = %{ merge(\%opts, $self->_master_connect_info_opts) };
b2e4d522 520
521# update
522 $r->[$i] = \%opts;
523 }
524
525 $self->$next($self->schema, @args);
955a6df6 526};
2bf79155 527
2bf79155 528=head2 all_storages
529
530Returns an array of of all the connected storage backends. The first element
531in the returned array is the master, and the remainings are each of the
532replicants.
533
534=cut
535
536sub all_storages {
64cdad22 537 my $self = shift @_;
538 return grep {defined $_ && blessed $_} (
539 $self->master,
6412a592 540 values %{ $self->replicants },
64cdad22 541 );
2bf79155 542}
543
c4d3fae2 544=head2 execute_reliably ($coderef, ?@args)
545
546Given a coderef, saves the current state of the L</read_handler>, forces it to
547use reliable storage (ie sets it to the master), executes a coderef and then
548restores the original state.
549
550Example:
551
64cdad22 552 my $reliably = sub {
553 my $name = shift @_;
554 $schema->resultset('User')->create({name=>$name});
555 my $user_rs = $schema->resultset('User')->find({name=>$name});
556 return $user_rs;
557 };
c4d3fae2 558
64cdad22 559 my $user_rs = $schema->storage->execute_reliably($reliably, 'John');
c4d3fae2 560
561Use this when you must be certain of your database state, such as when you just
562inserted something and need to get a resultset including it, etc.
563
564=cut
565
566sub execute_reliably {
64cdad22 567 my ($self, $coderef, @args) = @_;
568
569 unless( ref $coderef eq 'CODE') {
570 $self->throw_exception('Second argument must be a coderef');
571 }
572
573 ##Get copy of master storage
574 my $master = $self->master;
575
576 ##Get whatever the current read hander is
577 my $current = $self->read_handler;
578
579 ##Set the read handler to master
580 $self->read_handler($master);
581
582 ## do whatever the caller needs
583 my @result;
584 my $want_array = wantarray;
585
586 eval {
587 if($want_array) {
588 @result = $coderef->(@args);
589 } elsif(defined $want_array) {
590 ($result[0]) = ($coderef->(@args));
ed213e85 591 } else {
64cdad22 592 $coderef->(@args);
593 }
594 };
595
596 ##Reset to the original state
597 $self->read_handler($current);
598
599 ##Exception testing has to come last, otherwise you might leave the
600 ##read_handler set to master.
601
602 if($@) {
603 $self->throw_exception("coderef returned an error: $@");
604 } else {
605 return $want_array ? @result : $result[0];
606 }
c4d3fae2 607}
608
cb6ec758 609=head2 set_reliable_storage
610
611Sets the current $schema to be 'reliable', that is all queries, both read and
612write are sent to the master
64cdad22 613
cb6ec758 614=cut
615
616sub set_reliable_storage {
64cdad22 617 my $self = shift @_;
618 my $schema = $self->schema;
619 my $write_handler = $self->schema->storage->write_handler;
620
621 $schema->storage->read_handler($write_handler);
cb6ec758 622}
623
624=head2 set_balanced_storage
625
626Sets the current $schema to be use the </balancer> for all reads, while all
627writea are sent to the master only
64cdad22 628
cb6ec758 629=cut
630
631sub set_balanced_storage {
64cdad22 632 my $self = shift @_;
633 my $schema = $self->schema;
bd5da369 634 my $balanced_handler = $self->schema->storage->balancer;
64cdad22 635
bd5da369 636 $schema->storage->read_handler($balanced_handler);
cb6ec758 637}
2bf79155 638
639=head2 connected
640
641Check that the master and at least one of the replicants is connected.
642
643=cut
644
645sub connected {
64cdad22 646 my $self = shift @_;
647 return
648 $self->master->connected &&
649 $self->pool->connected_replicants;
2bf79155 650}
651
2bf79155 652=head2 ensure_connected
653
654Make sure all the storages are connected.
655
656=cut
657
658sub ensure_connected {
64cdad22 659 my $self = shift @_;
660 foreach my $source ($self->all_storages) {
661 $source->ensure_connected(@_);
662 }
2bf79155 663}
664
2bf79155 665=head2 limit_dialect
666
667Set the limit_dialect for all existing storages
668
669=cut
670
671sub limit_dialect {
64cdad22 672 my $self = shift @_;
673 foreach my $source ($self->all_storages) {
674 $source->limit_dialect(@_);
675 }
3fbe08e3 676 return $self->master->quote_char;
2bf79155 677}
678
2bf79155 679=head2 quote_char
680
681Set the quote_char for all existing storages
682
683=cut
684
685sub quote_char {
64cdad22 686 my $self = shift @_;
687 foreach my $source ($self->all_storages) {
688 $source->quote_char(@_);
689 }
3fbe08e3 690 return $self->master->quote_char;
2bf79155 691}
692
2bf79155 693=head2 name_sep
694
695Set the name_sep for all existing storages
696
697=cut
698
699sub name_sep {
64cdad22 700 my $self = shift @_;
701 foreach my $source ($self->all_storages) {
702 $source->name_sep(@_);
703 }
3fbe08e3 704 return $self->master->name_sep;
2bf79155 705}
706
2bf79155 707=head2 set_schema
708
709Set the schema object for all existing storages
710
711=cut
712
713sub set_schema {
64cdad22 714 my $self = shift @_;
715 foreach my $source ($self->all_storages) {
716 $source->set_schema(@_);
717 }
2bf79155 718}
719
2bf79155 720=head2 debug
721
722set a debug flag across all storages
723
724=cut
725
726sub debug {
64cdad22 727 my $self = shift @_;
3fbe08e3 728 if(@_) {
729 foreach my $source ($self->all_storages) {
730 $source->debug(@_);
731 }
64cdad22 732 }
3fbe08e3 733 return $self->master->debug;
2bf79155 734}
735
2bf79155 736=head2 debugobj
737
738set a debug object across all storages
739
740=cut
741
742sub debugobj {
64cdad22 743 my $self = shift @_;
3fbe08e3 744 if(@_) {
745 foreach my $source ($self->all_storages) {
746 $source->debugobj(@_);
747 }
64cdad22 748 }
3fbe08e3 749 return $self->master->debugobj;
2bf79155 750}
751
2bf79155 752=head2 debugfh
753
754set a debugfh object across all storages
755
756=cut
757
758sub debugfh {
64cdad22 759 my $self = shift @_;
3fbe08e3 760 if(@_) {
761 foreach my $source ($self->all_storages) {
762 $source->debugfh(@_);
763 }
64cdad22 764 }
3fbe08e3 765 return $self->master->debugfh;
2bf79155 766}
767
2bf79155 768=head2 debugcb
769
770set a debug callback across all storages
771
772=cut
773
774sub debugcb {
64cdad22 775 my $self = shift @_;
3fbe08e3 776 if(@_) {
777 foreach my $source ($self->all_storages) {
778 $source->debugcb(@_);
779 }
64cdad22 780 }
3fbe08e3 781 return $self->master->debugcb;
2bf79155 782}
783
2bf79155 784=head2 disconnect
785
786disconnect everything
787
788=cut
789
790sub disconnect {
64cdad22 791 my $self = shift @_;
792 foreach my $source ($self->all_storages) {
793 $source->disconnect(@_);
794 }
2bf79155 795}
796
b2e4d522 797=head2 cursor_class
798
799set cursor class on all storages, or return master's
800
801=cut
802
803sub cursor_class {
804 my ($self, $cursor_class) = @_;
805
806 if ($cursor_class) {
807 $_->cursor_class($cursor_class) for $self->all_storages;
808 }
809 $self->master->cursor_class;
810}
811
7e38d850 812=head1 GOTCHAS
813
814Due to the fact that replicants can lag behind a master, you must take care to
815make sure you use one of the methods to force read queries to a master should
816you need realtime data integrity. For example, if you insert a row, and then
817immediately re-read it from the database (say, by doing $row->discard_changes)
818or you insert a row and then immediately build a query that expects that row
819to be an item, you should force the master to handle reads. Otherwise, due to
820the lag, there is no certainty your data will be in the expected state.
821
822For data integrity, all transactions automatically use the master storage for
823all read and write queries. Using a transaction is the preferred and recommended
824method to force the master to handle all read queries.
825
826Otherwise, you can force a single query to use the master with the 'force_pool'
827attribute:
828
829 my $row = $resultset->search(undef, {force_pool=>'master'})->find($pk);
830
831This attribute will safely be ignore by non replicated storages, so you can use
832the same code for both types of systems.
833
834Lastly, you can use the L</execute_reliably> method, which works very much like
835a transaction.
836
837For debugging, you can turn replication on/off with the methods L</set_reliable_storage>
838and L</set_balanced_storage>, however this operates at a global level and is not
839suitable if you have a shared Schema object being used by multiple processes,
840such as on a web application server. You can get around this limitation by
841using the Schema clone method.
842
843 my $new_schema = $schema->clone;
844 $new_schema->set_reliable_storage;
845
846 ## $new_schema will use only the Master storage for all reads/writes while
847 ## the $schema object will use replicated storage.
848
f5d3a5de 849=head1 AUTHOR
850
64cdad22 851 John Napiorkowski <john.napiorkowski@takkle.com>
f5d3a5de 852
c4d3fae2 853Based on code originated by:
f5d3a5de 854
64cdad22 855 Norbert Csongrádi <bert@cpan.org>
856 Peter Siklósi <einon@einon.hu>
2156bbdd 857
f5d3a5de 858=head1 LICENSE
859
860You may distribute this code under the same terms as Perl itself.
861
862=cut
863
c354902c 864__PACKAGE__->meta->make_immutable;
865
f5d3a5de 8661;