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