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