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