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