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