Make the insert_returning capability private (and saner naming)
[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';
e666c5fd 17use Hash::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
48580715 29storage type, add some replicated (read-only) 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.
48580715 78Individual DBs may have quirks we are not aware of. Please use this in first
7e38d850 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
48580715 92Read traffic is spread across the replicants (slaves) occurring to a user
2bf79155 93selected algorithm. The default algorithm is random weighted.
94
bca099a3 95=head1 NOTES
96
48580715 97The consistency between 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
227d8366 309 _supports_insert_returning
6f7344b8 310 _count_select
311 _subq_count_select
312 _subq_update_delete
313 svp_rollback
314 svp_begin
315 svp_release
e398f77e 316 relname_to_table_alias
317 _straight_join_to_node
3244fdcc 318 _dbh_last_insert_id
319 _fix_bind_params
320 _default_dbi_connect_attributes
321 _dbi_connect_info
322 auto_savepoint
323 _sqlt_version_ok
324 _query_end
325 bind_attribute_by_data_type
326 transaction_depth
327 _dbh
328 _select_args
329 _dbh_execute_array
330 _sql_maker_args
331 _sql_maker
332 _query_start
333 _sqlt_version_error
334 _per_row_update_delete
335 _dbh_begin_work
336 _dbh_execute_inserts_with_no_binds
337 _select_args_to_query
338 _svp_generate_name
339 _multipk_update_delete
340 source_bind_attributes
341 _normalize_connect_info
342 _parse_connect_do
343 _dbh_commit
344 _execute_array
345 _placeholders_supported
346 _verify_pid
347 savepoints
348 _sqlt_minimum_version
349 _sql_maker_opts
350 _conn_pid
351 _typeless_placeholders_supported
352 _conn_tid
353 _dbh_autocommit
354 _native_data_type
355 _get_dbh
356 sql_maker_class
357 _dbh_rollback
358 _adjust_select_args_for_complex_prefetch
359 _resolve_ident_sources
360 _resolve_column_info
361 _prune_unused_joins
362 _strip_cond_qualifiers
363 _parse_order_by
364 _resolve_aliastypes_from_select_args
365 _execute
366 _do_query
367 _dbh_sth
368 _dbh_execute
6ed1cd2e 369 _prefetch_insert_auto_nextvals
64cdad22 370 /],
cb6ec758 371);
372
b2e4d522 373has _master_connect_info_opts =>
374 (is => 'rw', isa => HashRef, default => sub { {} });
375
376=head2 around: connect_info
377
48580715 378Preserves master's C<connect_info> options (for merging with replicants.)
379Also sets any Replicated-related options from connect_info, such as
dcdf7b2c 380C<pool_type>, C<pool_args>, C<balancer_type> and C<balancer_args>.
b2e4d522 381
382=cut
383
384around connect_info => sub {
385 my ($next, $self, $info, @extra) = @_;
386
0ce2d0d5 387 my $wantarray = wantarray;
388
282a9a4f 389 my $merge = Hash::Merge->new('LEFT_PRECEDENT');
e666c5fd 390
b2e4d522 391 my %opts;
392 for my $arg (@$info) {
393 next unless (reftype($arg)||'') eq 'HASH';
e666c5fd 394 %opts = %{ $merge->merge($arg, \%opts) };
b2e4d522 395 }
b2e4d522 396 delete $opts{dsn};
397
dcdf7b2c 398 if (@opts{qw/pool_type pool_args/}) {
399 $self->pool_type(delete $opts{pool_type})
400 if $opts{pool_type};
401
b88b85e7 402 $self->pool_args(
e666c5fd 403 $merge->merge((delete $opts{pool_args} || {}), $self->pool_args)
b88b85e7 404 );
dcdf7b2c 405
67c43863 406 $self->pool($self->_build_pool)
6f7344b8 407 if $self->pool;
dcdf7b2c 408 }
409
410 if (@opts{qw/balancer_type balancer_args/}) {
411 $self->balancer_type(delete $opts{balancer_type})
412 if $opts{balancer_type};
413
b88b85e7 414 $self->balancer_args(
e666c5fd 415 $merge->merge((delete $opts{balancer_args} || {}), $self->balancer_args)
b88b85e7 416 );
dcdf7b2c 417
67c43863 418 $self->balancer($self->_build_balancer)
6f7344b8 419 if $self->balancer;
dcdf7b2c 420 }
421
b2e4d522 422 $self->_master_connect_info_opts(\%opts);
423
0ce2d0d5 424 my (@res, $res);
425 if ($wantarray) {
426 @res = $self->$next($info, @extra);
427 } else {
428 $res = $self->$next($info, @extra);
429 }
430
fd4eb9c2 431 # Make sure master is blessed into the correct class and apply role to it.
432 my $master = $self->master;
433 $master->_determine_driver;
434 Moose::Meta::Class->initialize(ref $master);
cea43436 435
ec0946db 436 DBIx::Class::Storage::DBI::Replicated::WithDSN->meta->apply($master);
cea43436 437
438 # link pool back to master
439 $self->pool->master($master);
0ce2d0d5 440
441 $wantarray ? @res : $res;
b2e4d522 442};
443
26ab719a 444=head1 METHODS
2bf79155 445
26ab719a 446This class defines the following methods.
2bf79155 447
c354902c 448=head2 BUILDARGS
2bf79155 449
faaba25f 450L<DBIx::Class::Schema> when instantiating its storage passed itself as the
2ce6e9a6 451first argument. So we need to massage the arguments a bit so that all the
452bits get put into the correct places.
2bf79155 453
454=cut
455
c354902c 456sub BUILDARGS {
d7a58a29 457 my ($class, $schema, $storage_type_args, @args) = @_;
d4daee7b 458
c354902c 459 return {
6f7344b8 460 schema=>$schema,
461 %$storage_type_args,
462 @args
c354902c 463 }
464}
2bf79155 465
cb6ec758 466=head2 _build_master
2bf79155 467
cb6ec758 468Lazy builder for the L</master> attribute.
2bf79155 469
470=cut
471
cb6ec758 472sub _build_master {
2ce6e9a6 473 my $self = shift @_;
ee356d00 474 my $master = DBIx::Class::Storage::DBI->new($self->schema);
ee356d00 475 $master
106d5f3b 476}
477
26ab719a 478=head2 _build_pool
2bf79155 479
26ab719a 480Lazy builder for the L</pool> attribute.
2bf79155 481
482=cut
483
26ab719a 484sub _build_pool {
64cdad22 485 my $self = shift @_;
486 $self->create_pool(%{$self->pool_args});
2bf79155 487}
488
26ab719a 489=head2 _build_balancer
2bf79155 490
cb6ec758 491Lazy builder for the L</balancer> attribute. This takes a Pool object so that
492the balancer knows which pool it's balancing.
2bf79155 493
494=cut
495
26ab719a 496sub _build_balancer {
64cdad22 497 my $self = shift @_;
498 $self->create_balancer(
6f7344b8 499 pool=>$self->pool,
64cdad22 500 master=>$self->master,
501 %{$self->balancer_args},
502 );
2bf79155 503}
504
cb6ec758 505=head2 _build_write_handler
2bf79155 506
cb6ec758 507Lazy builder for the L</write_handler> attribute. The default is to set this to
508the L</master>.
50336325 509
510=cut
511
cb6ec758 512sub _build_write_handler {
64cdad22 513 return shift->master;
cb6ec758 514}
50336325 515
cb6ec758 516=head2 _build_read_handler
2bf79155 517
cb6ec758 518Lazy builder for the L</read_handler> attribute. The default is to set this to
519the L</balancer>.
2bf79155 520
521=cut
522
cb6ec758 523sub _build_read_handler {
64cdad22 524 return shift->balancer;
cb6ec758 525}
50336325 526
cb6ec758 527=head2 around: connect_replicants
2bf79155 528
cb6ec758 529All calls to connect_replicants needs to have an existing $schema tacked onto
b2e4d522 530top of the args, since L<DBIx::Storage::DBI> needs it, and any C<connect_info>
531options merged with the master, with replicant opts having higher priority.
955a6df6 532
cb6ec758 533=cut
955a6df6 534
b2e4d522 535around connect_replicants => sub {
536 my ($next, $self, @args) = @_;
537
538 for my $r (@args) {
539 $r = [ $r ] unless reftype $r eq 'ARRAY';
540
1a58752c 541 $self->throw_exception('coderef replicant connect_info not supported')
b2e4d522 542 if ref $r->[0] && reftype $r->[0] eq 'CODE';
543
544# any connect_info options?
545 my $i = 0;
546 $i++ while $i < @$r && (reftype($r->[$i])||'') ne 'HASH';
547
6f7344b8 548# make one if none
b2e4d522 549 $r->[$i] = {} unless $r->[$i];
550
551# merge if two hashes
b88b85e7 552 my @hashes = @$r[$i .. $#{$r}];
553
1a58752c 554 $self->throw_exception('invalid connect_info options')
b88b85e7 555 if (grep { reftype($_) eq 'HASH' } @hashes) != @hashes;
556
1a58752c 557 $self->throw_exception('too many hashrefs in connect_info')
b88b85e7 558 if @hashes > 2;
559
282a9a4f 560 my $merge = Hash::Merge->new('LEFT_PRECEDENT');
e666c5fd 561 my %opts = %{ $merge->merge(reverse @hashes) };
b88b85e7 562
563# delete them
b2e4d522 564 splice @$r, $i+1, ($#{$r} - $i), ();
565
0bd8e058 566# make sure master/replicants opts don't clash
567 my %master_opts = %{ $self->_master_connect_info_opts };
568 if (exists $opts{dbh_maker}) {
569 delete @master_opts{qw/dsn user password/};
570 }
571 delete $master_opts{dbh_maker};
572
b2e4d522 573# merge with master
e666c5fd 574 %opts = %{ $merge->merge(\%opts, \%master_opts) };
b2e4d522 575
576# update
577 $r->[$i] = \%opts;
578 }
579
580 $self->$next($self->schema, @args);
955a6df6 581};
2bf79155 582
2bf79155 583=head2 all_storages
584
585Returns an array of of all the connected storage backends. The first element
586in the returned array is the master, and the remainings are each of the
587replicants.
588
589=cut
590
591sub all_storages {
64cdad22 592 my $self = shift @_;
593 return grep {defined $_ && blessed $_} (
594 $self->master,
6412a592 595 values %{ $self->replicants },
64cdad22 596 );
2bf79155 597}
598
c4d3fae2 599=head2 execute_reliably ($coderef, ?@args)
600
601Given a coderef, saves the current state of the L</read_handler>, forces it to
48580715 602use reliable storage (e.g. sets it to the master), executes a coderef and then
c4d3fae2 603restores the original state.
604
605Example:
606
64cdad22 607 my $reliably = sub {
608 my $name = shift @_;
609 $schema->resultset('User')->create({name=>$name});
610 my $user_rs = $schema->resultset('User')->find({name=>$name});
611 return $user_rs;
612 };
c4d3fae2 613
64cdad22 614 my $user_rs = $schema->storage->execute_reliably($reliably, 'John');
c4d3fae2 615
616Use this when you must be certain of your database state, such as when you just
617inserted something and need to get a resultset including it, etc.
618
619=cut
620
621sub execute_reliably {
64cdad22 622 my ($self, $coderef, @args) = @_;
d4daee7b 623
64cdad22 624 unless( ref $coderef eq 'CODE') {
625 $self->throw_exception('Second argument must be a coderef');
626 }
d4daee7b 627
64cdad22 628 ##Get copy of master storage
629 my $master = $self->master;
d4daee7b 630
64cdad22 631 ##Get whatever the current read hander is
632 my $current = $self->read_handler;
d4daee7b 633
64cdad22 634 ##Set the read handler to master
635 $self->read_handler($master);
d4daee7b 636
64cdad22 637 ## do whatever the caller needs
638 my @result;
639 my $want_array = wantarray;
d4daee7b 640
64cdad22 641 eval {
642 if($want_array) {
643 @result = $coderef->(@args);
644 } elsif(defined $want_array) {
645 ($result[0]) = ($coderef->(@args));
ed213e85 646 } else {
64cdad22 647 $coderef->(@args);
6f7344b8 648 }
64cdad22 649 };
d4daee7b 650
64cdad22 651 ##Reset to the original state
6f7344b8 652 $self->read_handler($current);
d4daee7b 653
64cdad22 654 ##Exception testing has to come last, otherwise you might leave the
655 ##read_handler set to master.
d4daee7b 656
64cdad22 657 if($@) {
658 $self->throw_exception("coderef returned an error: $@");
659 } else {
660 return $want_array ? @result : $result[0];
661 }
c4d3fae2 662}
663
cb6ec758 664=head2 set_reliable_storage
665
666Sets the current $schema to be 'reliable', that is all queries, both read and
667write are sent to the master
d4daee7b 668
cb6ec758 669=cut
670
671sub set_reliable_storage {
64cdad22 672 my $self = shift @_;
673 my $schema = $self->schema;
674 my $write_handler = $self->schema->storage->write_handler;
d4daee7b 675
64cdad22 676 $schema->storage->read_handler($write_handler);
cb6ec758 677}
678
679=head2 set_balanced_storage
680
681Sets the current $schema to be use the </balancer> for all reads, while all
48580715 682writes are sent to the master only
d4daee7b 683
cb6ec758 684=cut
685
686sub set_balanced_storage {
64cdad22 687 my $self = shift @_;
688 my $schema = $self->schema;
bd5da369 689 my $balanced_handler = $self->schema->storage->balancer;
d4daee7b 690
bd5da369 691 $schema->storage->read_handler($balanced_handler);
cb6ec758 692}
2bf79155 693
694=head2 connected
695
696Check that the master and at least one of the replicants is connected.
697
698=cut
699
700sub connected {
64cdad22 701 my $self = shift @_;
702 return
703 $self->master->connected &&
704 $self->pool->connected_replicants;
2bf79155 705}
706
2bf79155 707=head2 ensure_connected
708
709Make sure all the storages are connected.
710
711=cut
712
713sub ensure_connected {
64cdad22 714 my $self = shift @_;
715 foreach my $source ($self->all_storages) {
716 $source->ensure_connected(@_);
717 }
2bf79155 718}
719
2bf79155 720=head2 limit_dialect
721
722Set the limit_dialect for all existing storages
723
724=cut
725
726sub limit_dialect {
64cdad22 727 my $self = shift @_;
728 foreach my $source ($self->all_storages) {
729 $source->limit_dialect(@_);
730 }
3fbe08e3 731 return $self->master->quote_char;
2bf79155 732}
733
2bf79155 734=head2 quote_char
735
736Set the quote_char for all existing storages
737
738=cut
739
740sub quote_char {
64cdad22 741 my $self = shift @_;
742 foreach my $source ($self->all_storages) {
743 $source->quote_char(@_);
744 }
3fbe08e3 745 return $self->master->quote_char;
2bf79155 746}
747
2bf79155 748=head2 name_sep
749
750Set the name_sep for all existing storages
751
752=cut
753
754sub name_sep {
64cdad22 755 my $self = shift @_;
756 foreach my $source ($self->all_storages) {
757 $source->name_sep(@_);
758 }
3fbe08e3 759 return $self->master->name_sep;
2bf79155 760}
761
2bf79155 762=head2 set_schema
763
764Set the schema object for all existing storages
765
766=cut
767
768sub set_schema {
64cdad22 769 my $self = shift @_;
770 foreach my $source ($self->all_storages) {
771 $source->set_schema(@_);
772 }
2bf79155 773}
774
2bf79155 775=head2 debug
776
777set a debug flag across all storages
778
779=cut
780
781sub debug {
64cdad22 782 my $self = shift @_;
3fbe08e3 783 if(@_) {
784 foreach my $source ($self->all_storages) {
785 $source->debug(@_);
6f7344b8 786 }
64cdad22 787 }
3fbe08e3 788 return $self->master->debug;
2bf79155 789}
790
2bf79155 791=head2 debugobj
792
cea43436 793set a debug object
2bf79155 794
795=cut
796
797sub debugobj {
64cdad22 798 my $self = shift @_;
cea43436 799 return $self->master->debugobj(@_);
2bf79155 800}
801
2bf79155 802=head2 debugfh
803
cea43436 804set a debugfh object
2bf79155 805
806=cut
807
808sub debugfh {
64cdad22 809 my $self = shift @_;
cea43436 810 return $self->master->debugfh(@_);
2bf79155 811}
812
2bf79155 813=head2 debugcb
814
cea43436 815set a debug callback
2bf79155 816
817=cut
818
819sub debugcb {
64cdad22 820 my $self = shift @_;
cea43436 821 return $self->master->debugcb(@_);
2bf79155 822}
823
2bf79155 824=head2 disconnect
825
826disconnect everything
827
828=cut
829
830sub disconnect {
64cdad22 831 my $self = shift @_;
832 foreach my $source ($self->all_storages) {
833 $source->disconnect(@_);
834 }
2bf79155 835}
836
b2e4d522 837=head2 cursor_class
838
839set cursor class on all storages, or return master's
840
841=cut
842
843sub cursor_class {
844 my ($self, $cursor_class) = @_;
845
846 if ($cursor_class) {
847 $_->cursor_class($cursor_class) for $self->all_storages;
848 }
849 $self->master->cursor_class;
850}
d4daee7b 851
3244fdcc 852=head2 cursor
853
854set cursor class on all storages, or return master's, alias for L</cursor_class>
855above.
856
857=cut
858
859sub cursor {
860 my ($self, $cursor_class) = @_;
861
862 if ($cursor_class) {
863 $_->cursor($cursor_class) for $self->all_storages;
864 }
865 $self->master->cursor;
866}
867
868=head2 unsafe
869
870sets the L<DBIx::Class::Storage::DBI/unsafe> option on all storages or returns
871master's current setting
872
873=cut
874
875sub unsafe {
876 my $self = shift;
877
878 if (@_) {
879 $_->unsafe(@_) for $self->all_storages;
880 }
881
882 return $self->master->unsafe;
883}
884
885=head2 disable_sth_caching
886
887sets the L<DBIx::Class::Storage::DBI/disable_sth_caching> option on all storages
888or returns master's current setting
889
890=cut
891
892sub disable_sth_caching {
893 my $self = shift;
894
895 if (@_) {
896 $_->disable_sth_caching(@_) for $self->all_storages;
897 }
898
899 return $self->master->disable_sth_caching;
900}
901
902=head2 lag_behind_master
903
904returns the highest Replicant L<DBIx::Class::Storage::DBI/lag_behind_master>
905setting
906
907=cut
908
909sub lag_behind_master {
910 my $self = shift;
911
912 return max map $_->lag_behind_master, $self->replicants;
913}
914
915=head2 is_replicating
916
917returns true if all replicants return true for
918L<DBIx::Class::Storage::DBI/is_replicating>
919
920=cut
921
922sub is_replicating {
923 my $self = shift;
924
925 return (grep $_->is_replicating, $self->replicants) == ($self->replicants);
926}
927
928=head2 connect_call_datetime_setup
929
930calls L<DBIx::Class::Storage::DBI/connect_call_datetime_setup> for all storages
931
932=cut
933
934sub connect_call_datetime_setup {
935 my $self = shift;
936 $_->connect_call_datetime_setup for $self->all_storages;
937}
938
939sub _populate_dbh {
940 my $self = shift;
941 $_->_populate_dbh for $self->all_storages;
942}
943
944sub _connect {
945 my $self = shift;
946 $_->_connect for $self->all_storages;
947}
948
949sub _rebless {
950 my $self = shift;
951 $_->_rebless for $self->all_storages;
952}
953
954sub _determine_driver {
955 my $self = shift;
956 $_->_determine_driver for $self->all_storages;
957}
958
959sub _driver_determined {
960 my $self = shift;
961
962 if (@_) {
963 $_->_driver_determined(@_) for $self->all_storages;
964 }
965
966 return $self->master->_driver_determined;
967}
968
969sub _init {
970 my $self = shift;
971
972 $_->_init for $self->all_storages;
973}
974
975sub _run_connection_actions {
976 my $self = shift;
977
978 $_->_run_connection_actions for $self->all_storages;
979}
980
981sub _do_connection_actions {
982 my $self = shift;
983
984 if (@_) {
985 $_->_do_connection_actions(@_) for $self->all_storages;
986 }
987}
988
989sub connect_call_do_sql {
990 my $self = shift;
991 $_->connect_call_do_sql(@_) for $self->all_storages;
992}
993
994sub disconnect_call_do_sql {
995 my $self = shift;
996 $_->disconnect_call_do_sql(@_) for $self->all_storages;
997}
998
999sub _seems_connected {
1000 my $self = shift;
1001
1002 return min map $_->_seems_connected, $self->all_storages;
1003}
1004
1005sub _ping {
1006 my $self = shift;
1007
1008 return min map $_->_ping, $self->all_storages;
1009}
1010
7e38d850 1011=head1 GOTCHAS
1012
1013Due to the fact that replicants can lag behind a master, you must take care to
1014make sure you use one of the methods to force read queries to a master should
1015you need realtime data integrity. For example, if you insert a row, and then
1016immediately re-read it from the database (say, by doing $row->discard_changes)
1017or you insert a row and then immediately build a query that expects that row
1018to be an item, you should force the master to handle reads. Otherwise, due to
1019the lag, there is no certainty your data will be in the expected state.
1020
1021For data integrity, all transactions automatically use the master storage for
1022all read and write queries. Using a transaction is the preferred and recommended
1023method to force the master to handle all read queries.
1024
1025Otherwise, you can force a single query to use the master with the 'force_pool'
1026attribute:
1027
1028 my $row = $resultset->search(undef, {force_pool=>'master'})->find($pk);
1029
1030This attribute will safely be ignore by non replicated storages, so you can use
1031the same code for both types of systems.
1032
1033Lastly, you can use the L</execute_reliably> method, which works very much like
1034a transaction.
1035
1036For debugging, you can turn replication on/off with the methods L</set_reliable_storage>
1037and L</set_balanced_storage>, however this operates at a global level and is not
1038suitable if you have a shared Schema object being used by multiple processes,
1039such as on a web application server. You can get around this limitation by
1040using the Schema clone method.
1041
1042 my $new_schema = $schema->clone;
1043 $new_schema->set_reliable_storage;
d4daee7b 1044
7e38d850 1045 ## $new_schema will use only the Master storage for all reads/writes while
1046 ## the $schema object will use replicated storage.
1047
f5d3a5de 1048=head1 AUTHOR
1049
64cdad22 1050 John Napiorkowski <john.napiorkowski@takkle.com>
f5d3a5de 1051
c4d3fae2 1052Based on code originated by:
f5d3a5de 1053
64cdad22 1054 Norbert Csongrádi <bert@cpan.org>
1055 Peter Siklósi <einon@einon.hu>
2156bbdd 1056
f5d3a5de 1057=head1 LICENSE
1058
1059You may distribute this code under the same terms as Perl itself.
1060
1061=cut
1062
c354902c 1063__PACKAGE__->meta->make_immutable;
1064
f5d3a5de 10651;