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