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