Merge 'sybase' into 'trunk'
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Replicated.pm
CommitLineData
2156bbdd 1package DBIx::Class::Storage::DBI::Replicated;
f5d3a5de 2
ecb65397 3BEGIN {
4 use Carp::Clan qw/^DBIx::Class/;
d4daee7b 5
ecb65397 6 ## Modules required for Replication support not required for general DBIC
7 ## use, so we explicitly test for these.
d4daee7b 8
ecb65397 9 my %replication_required = (
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
0bd8e058 522# make sure master/replicants opts don't clash
523 my %master_opts = %{ $self->_master_connect_info_opts };
524 if (exists $opts{dbh_maker}) {
525 delete @master_opts{qw/dsn user password/};
526 }
527 delete $master_opts{dbh_maker};
528
b2e4d522 529# merge with master
0bd8e058 530 %opts = %{ merge(\%opts, \%master_opts) };
b2e4d522 531
532# update
533 $r->[$i] = \%opts;
534 }
535
536 $self->$next($self->schema, @args);
955a6df6 537};
2bf79155 538
2bf79155 539=head2 all_storages
540
541Returns an array of of all the connected storage backends. The first element
542in the returned array is the master, and the remainings are each of the
543replicants.
544
545=cut
546
547sub all_storages {
64cdad22 548 my $self = shift @_;
549 return grep {defined $_ && blessed $_} (
550 $self->master,
6412a592 551 values %{ $self->replicants },
64cdad22 552 );
2bf79155 553}
554
c4d3fae2 555=head2 execute_reliably ($coderef, ?@args)
556
557Given a coderef, saves the current state of the L</read_handler>, forces it to
558use reliable storage (ie sets it to the master), executes a coderef and then
559restores the original state.
560
561Example:
562
64cdad22 563 my $reliably = sub {
564 my $name = shift @_;
565 $schema->resultset('User')->create({name=>$name});
566 my $user_rs = $schema->resultset('User')->find({name=>$name});
567 return $user_rs;
568 };
c4d3fae2 569
64cdad22 570 my $user_rs = $schema->storage->execute_reliably($reliably, 'John');
c4d3fae2 571
572Use this when you must be certain of your database state, such as when you just
573inserted something and need to get a resultset including it, etc.
574
575=cut
576
577sub execute_reliably {
64cdad22 578 my ($self, $coderef, @args) = @_;
d4daee7b 579
64cdad22 580 unless( ref $coderef eq 'CODE') {
581 $self->throw_exception('Second argument must be a coderef');
582 }
d4daee7b 583
64cdad22 584 ##Get copy of master storage
585 my $master = $self->master;
d4daee7b 586
64cdad22 587 ##Get whatever the current read hander is
588 my $current = $self->read_handler;
d4daee7b 589
64cdad22 590 ##Set the read handler to master
591 $self->read_handler($master);
d4daee7b 592
64cdad22 593 ## do whatever the caller needs
594 my @result;
595 my $want_array = wantarray;
d4daee7b 596
64cdad22 597 eval {
598 if($want_array) {
599 @result = $coderef->(@args);
600 } elsif(defined $want_array) {
601 ($result[0]) = ($coderef->(@args));
ed213e85 602 } else {
64cdad22 603 $coderef->(@args);
6f7344b8 604 }
64cdad22 605 };
d4daee7b 606
64cdad22 607 ##Reset to the original state
6f7344b8 608 $self->read_handler($current);
d4daee7b 609
64cdad22 610 ##Exception testing has to come last, otherwise you might leave the
611 ##read_handler set to master.
d4daee7b 612
64cdad22 613 if($@) {
614 $self->throw_exception("coderef returned an error: $@");
615 } else {
616 return $want_array ? @result : $result[0];
617 }
c4d3fae2 618}
619
cb6ec758 620=head2 set_reliable_storage
621
622Sets the current $schema to be 'reliable', that is all queries, both read and
623write are sent to the master
d4daee7b 624
cb6ec758 625=cut
626
627sub set_reliable_storage {
64cdad22 628 my $self = shift @_;
629 my $schema = $self->schema;
630 my $write_handler = $self->schema->storage->write_handler;
d4daee7b 631
64cdad22 632 $schema->storage->read_handler($write_handler);
cb6ec758 633}
634
635=head2 set_balanced_storage
636
637Sets the current $schema to be use the </balancer> for all reads, while all
638writea are sent to the master only
d4daee7b 639
cb6ec758 640=cut
641
642sub set_balanced_storage {
64cdad22 643 my $self = shift @_;
644 my $schema = $self->schema;
bd5da369 645 my $balanced_handler = $self->schema->storage->balancer;
d4daee7b 646
bd5da369 647 $schema->storage->read_handler($balanced_handler);
cb6ec758 648}
2bf79155 649
650=head2 connected
651
652Check that the master and at least one of the replicants is connected.
653
654=cut
655
656sub connected {
64cdad22 657 my $self = shift @_;
658 return
659 $self->master->connected &&
660 $self->pool->connected_replicants;
2bf79155 661}
662
2bf79155 663=head2 ensure_connected
664
665Make sure all the storages are connected.
666
667=cut
668
669sub ensure_connected {
64cdad22 670 my $self = shift @_;
671 foreach my $source ($self->all_storages) {
672 $source->ensure_connected(@_);
673 }
2bf79155 674}
675
2bf79155 676=head2 limit_dialect
677
678Set the limit_dialect for all existing storages
679
680=cut
681
682sub limit_dialect {
64cdad22 683 my $self = shift @_;
684 foreach my $source ($self->all_storages) {
685 $source->limit_dialect(@_);
686 }
3fbe08e3 687 return $self->master->quote_char;
2bf79155 688}
689
2bf79155 690=head2 quote_char
691
692Set the quote_char for all existing storages
693
694=cut
695
696sub quote_char {
64cdad22 697 my $self = shift @_;
698 foreach my $source ($self->all_storages) {
699 $source->quote_char(@_);
700 }
3fbe08e3 701 return $self->master->quote_char;
2bf79155 702}
703
2bf79155 704=head2 name_sep
705
706Set the name_sep for all existing storages
707
708=cut
709
710sub name_sep {
64cdad22 711 my $self = shift @_;
712 foreach my $source ($self->all_storages) {
713 $source->name_sep(@_);
714 }
3fbe08e3 715 return $self->master->name_sep;
2bf79155 716}
717
2bf79155 718=head2 set_schema
719
720Set the schema object for all existing storages
721
722=cut
723
724sub set_schema {
64cdad22 725 my $self = shift @_;
726 foreach my $source ($self->all_storages) {
727 $source->set_schema(@_);
728 }
2bf79155 729}
730
2bf79155 731=head2 debug
732
733set a debug flag across all storages
734
735=cut
736
737sub debug {
64cdad22 738 my $self = shift @_;
3fbe08e3 739 if(@_) {
740 foreach my $source ($self->all_storages) {
741 $source->debug(@_);
6f7344b8 742 }
64cdad22 743 }
3fbe08e3 744 return $self->master->debug;
2bf79155 745}
746
2bf79155 747=head2 debugobj
748
749set a debug object across all storages
750
751=cut
752
753sub debugobj {
64cdad22 754 my $self = shift @_;
3fbe08e3 755 if(@_) {
756 foreach my $source ($self->all_storages) {
757 $source->debugobj(@_);
6f7344b8 758 }
64cdad22 759 }
3fbe08e3 760 return $self->master->debugobj;
2bf79155 761}
762
2bf79155 763=head2 debugfh
764
765set a debugfh object across all storages
766
767=cut
768
769sub debugfh {
64cdad22 770 my $self = shift @_;
3fbe08e3 771 if(@_) {
772 foreach my $source ($self->all_storages) {
773 $source->debugfh(@_);
6f7344b8 774 }
64cdad22 775 }
3fbe08e3 776 return $self->master->debugfh;
2bf79155 777}
778
2bf79155 779=head2 debugcb
780
781set a debug callback across all storages
782
783=cut
784
785sub debugcb {
64cdad22 786 my $self = shift @_;
3fbe08e3 787 if(@_) {
788 foreach my $source ($self->all_storages) {
789 $source->debugcb(@_);
6f7344b8 790 }
64cdad22 791 }
3fbe08e3 792 return $self->master->debugcb;
2bf79155 793}
794
2bf79155 795=head2 disconnect
796
797disconnect everything
798
799=cut
800
801sub disconnect {
64cdad22 802 my $self = shift @_;
803 foreach my $source ($self->all_storages) {
804 $source->disconnect(@_);
805 }
2bf79155 806}
807
b2e4d522 808=head2 cursor_class
809
810set cursor class on all storages, or return master's
811
812=cut
813
814sub cursor_class {
815 my ($self, $cursor_class) = @_;
816
817 if ($cursor_class) {
818 $_->cursor_class($cursor_class) for $self->all_storages;
819 }
820 $self->master->cursor_class;
821}
d4daee7b 822
7e38d850 823=head1 GOTCHAS
824
825Due to the fact that replicants can lag behind a master, you must take care to
826make sure you use one of the methods to force read queries to a master should
827you need realtime data integrity. For example, if you insert a row, and then
828immediately re-read it from the database (say, by doing $row->discard_changes)
829or you insert a row and then immediately build a query that expects that row
830to be an item, you should force the master to handle reads. Otherwise, due to
831the lag, there is no certainty your data will be in the expected state.
832
833For data integrity, all transactions automatically use the master storage for
834all read and write queries. Using a transaction is the preferred and recommended
835method to force the master to handle all read queries.
836
837Otherwise, you can force a single query to use the master with the 'force_pool'
838attribute:
839
840 my $row = $resultset->search(undef, {force_pool=>'master'})->find($pk);
841
842This attribute will safely be ignore by non replicated storages, so you can use
843the same code for both types of systems.
844
845Lastly, you can use the L</execute_reliably> method, which works very much like
846a transaction.
847
848For debugging, you can turn replication on/off with the methods L</set_reliable_storage>
849and L</set_balanced_storage>, however this operates at a global level and is not
850suitable if you have a shared Schema object being used by multiple processes,
851such as on a web application server. You can get around this limitation by
852using the Schema clone method.
853
854 my $new_schema = $schema->clone;
855 $new_schema->set_reliable_storage;
d4daee7b 856
7e38d850 857 ## $new_schema will use only the Master storage for all reads/writes while
858 ## the $schema object will use replicated storage.
859
f5d3a5de 860=head1 AUTHOR
861
64cdad22 862 John Napiorkowski <john.napiorkowski@takkle.com>
f5d3a5de 863
c4d3fae2 864Based on code originated by:
f5d3a5de 865
64cdad22 866 Norbert Csongrádi <bert@cpan.org>
867 Peter Siklósi <einon@einon.hu>
2156bbdd 868
f5d3a5de 869=head1 LICENSE
870
871You may distribute this code under the same terms as Perl itself.
872
873=cut
874
c354902c 875__PACKAGE__->meta->make_immutable;
876
f5d3a5de 8771;