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