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