1 package DBIx::Class::Storage::DBI::Replicated;
5 use Moose::Util::TypeConstraints;
6 use DBIx::Class::Storage::DBI;
7 use DBIx::Class::Storage::DBI::Replicated::Pool;
8 use DBIx::Class::Storage::DBI::Replicated::Balancer;
12 DBIx::Class::Storage::DBI::Replicated - ALPHA Replicated database support
16 The Following example shows how to change an existing $schema to a replicated
17 storage type, add some replicated (readonly) databases, and perform reporting
20 ## Change storage_type in your schema class
21 $schema->storage_type( ['::DBI::Replicated', {balancer=>'::Random'}] );
23 ## Add some slaves. Basically this is an array of arrayrefs, where each
24 ## arrayref is database connect information
26 $schema->storage->connect_replicants(
27 [$dsn1, $user, $pass, \%opts],
28 [$dsn2, $user, $pass, \%opts],
29 [$dsn3, $user, $pass, \%opts],
34 Warning: This class is marked ALPHA. We are using this in development and have
35 some basic test coverage but the code hasn't yet been stressed by a variety
36 of databases. Individual DB's may have quirks we are not aware of. Please
37 use this in development and pass along your experiences/bug fixes.
39 This class implements replicated data store for DBI. Currently you can define
40 one master and numerous slave database connections. All write-type queries
41 (INSERT, UPDATE, DELETE and even LAST_INSERT_ID) are routed to master
42 database, all read-type queries (SELECTs) go to the slave database.
44 Basically, any method request that L<DBIx::Class::Storage::DBI> would normally
45 handle gets delegated to one of the two attributes: L</read_handler> or to
46 L</write_handler>. Additionally, some methods need to be distributed
47 to all existing storages. This way our storage class is a drop in replacement
48 for L<DBIx::Class::Storage::DBI>.
50 Read traffic is spread across the replicants (slaves) occuring to a user
51 selected algorithm. The default algorithm is random weighted.
55 The consistancy betweeen master and replicants is database specific. The Pool
56 gives you a method to validate it's replicants, removing and replacing them
57 when they fail/pass predefined criteria. It is recommened that your application
58 define two schemas, one using the replicated storage and another that just
59 connects to the master.
63 This class defines the following attributes.
67 The underlying L<DBIx::Class::Schema> object this storage is attaching
73 isa=>'DBIx::Class::Schema',
80 Contains the classname which will instantiate the L</pool> object. Defaults
81 to: L<DBIx::Class::Storage::DBI::Replicated::Pool>.
89 default=>'DBIx::Class::Storage::DBI::Replicated::Pool',
91 'create_pool' => 'new',
97 Contains a hashref of initialized information to pass to the Balancer object.
98 See L<DBIx::Class::Storage::Replicated::Pool> for available arguments.
113 The replication pool requires a balance class to provider the methods for
114 choose how to spread the query load across each replicant in the pool.
118 subtype 'DBIx::Class::Storage::DBI::Replicated::BalancerClassNamePart',
121 coerce 'DBIx::Class::Storage::DBI::Replicated::BalancerClassNamePart',
126 $type = 'DBIx::Class::Storage::DBI::Replicated::Balancer'.$type;
128 Class::MOP::load_class($type);
132 has 'balancer_type' => (
134 isa=>'DBIx::Class::Storage::DBI::Replicated::BalancerClassNamePart',
137 default=> 'DBIx::Class::Storage::DBI::Replicated::Balancer::First',
139 'create_balancer' => 'new',
145 Contains a hashref of initialized information to pass to the Balancer object.
146 See L<DBIx::Class::Storage::Replicated::Balancer> for available arguments.
150 has 'balancer_args' => (
160 Is a <DBIx::Class::Storage::DBI::Replicated::Pool> or derived class. This is a
161 container class for one or more replicated databases.
167 isa=>'DBIx::Class::Storage::DBI::Replicated::Pool',
178 Is a <DBIx::Class::Storage::DBI::Replicated::Balancer> or derived class. This
179 is a class that takes a pool (<DBIx::Class::Storage::DBI::Replicated::Pool>)
185 isa=>'DBIx::Class::Storage::DBI::Replicated::Balancer',
187 handles=>[qw/auto_validate_every/],
192 The master defines the canonical state for a pool of connected databases. All
193 the replicants are expected to match this databases state. Thus, in a classic
194 Master / Slaves distributed system, all the slaves are expected to replicate
195 the Master's state as quick as possible. This is the only database in the
196 pool of databases that is allowed to handle write traffic.
202 isa=>'DBIx::Class::Storage::DBI',
206 =head1 ATTRIBUTES IMPLEMENTING THE DBIx::Storage::DBI INTERFACE
208 The following methods are delegated all the methods required for the
209 L<DBIx::Class::Storage::DBI> interface.
213 Defines an object that implements the read side of L<BIx::Class::Storage::DBI>.
217 has 'read_handler' => (
230 Defines an object that implements the write side of L<BIx::Class::Storage::DBI>.
234 has 'write_handler' => (
247 deployment_statements
273 This class defines the following methods.
277 L<DBIx::Class::Schema> when instantiating it's storage passed itself as the
278 first argument. So we need to massage the arguments a bit so that all the
279 bits get put into the correct places.
283 around 'new' => sub {
284 my ($new, $self, $schema, $storage_type_args, @args) = @_;
285 return $self->$new(schema=>$schema, %$storage_type_args, @args);
290 Lazy builder for the L</master> attribute.
296 DBIx::Class::Storage::DBI->new($self->schema);
301 Lazy builder for the L</pool> attribute.
307 $self->create_pool(%{$self->pool_args});
310 =head2 _build_balancer
312 Lazy builder for the L</balancer> attribute. This takes a Pool object so that
313 the balancer knows which pool it's balancing.
317 sub _build_balancer {
319 $self->create_balancer(
321 master=>$self->master,
322 %{$self->balancer_args},
326 =head2 _build_write_handler
328 Lazy builder for the L</write_handler> attribute. The default is to set this to
333 sub _build_write_handler {
334 return shift->master;
337 =head2 _build_read_handler
339 Lazy builder for the L</read_handler> attribute. The default is to set this to
344 sub _build_read_handler {
345 return shift->balancer;
348 =head2 around: connect_replicants
350 All calls to connect_replicants needs to have an existing $schema tacked onto
351 top of the args, since L<DBIx::Storage::DBI> needs it.
355 around 'connect_replicants' => sub {
356 my ($method, $self, @args) = @_;
357 $self->$method($self->schema, @args);
362 Returns an array of of all the connected storage backends. The first element
363 in the returned array is the master, and the remainings are each of the
370 return grep {defined $_ && blessed $_} (
376 =head2 execute_reliably ($coderef, ?@args)
378 Given a coderef, saves the current state of the L</read_handler>, forces it to
379 use reliable storage (ie sets it to the master), executes a coderef and then
380 restores the original state.
386 $schema->resultset('User')->create({name=>$name});
387 my $user_rs = $schema->resultset('User')->find({name=>$name});
391 my $user_rs = $schema->storage->execute_reliably($reliably, 'John');
393 Use this when you must be certain of your database state, such as when you just
394 inserted something and need to get a resultset including it, etc.
398 sub execute_reliably {
399 my ($self, $coderef, @args) = @_;
401 unless( ref $coderef eq 'CODE') {
402 $self->throw_exception('Second argument must be a coderef');
405 ##Get copy of master storage
406 my $master = $self->master;
408 ##Get whatever the current read hander is
409 my $current = $self->read_handler;
411 ##Set the read handler to master
412 $self->read_handler($master);
414 ## do whatever the caller needs
416 my $want_array = wantarray;
420 @result = $coderef->(@args);
421 } elsif(defined $want_array) {
422 ($result[0]) = ($coderef->(@args));
428 ##Reset to the original state
429 $self->read_handler($current);
431 ##Exception testing has to come last, otherwise you might leave the
432 ##read_handler set to master.
435 $self->throw_exception("coderef returned an error: $@");
437 return $want_array ? @result : $result[0];
441 =head2 set_reliable_storage
443 Sets the current $schema to be 'reliable', that is all queries, both read and
444 write are sent to the master
448 sub set_reliable_storage {
450 my $schema = $self->schema;
451 my $write_handler = $self->schema->storage->write_handler;
453 $schema->storage->read_handler($write_handler);
456 =head2 set_balanced_storage
458 Sets the current $schema to be use the </balancer> for all reads, while all
459 writea are sent to the master only
463 sub set_balanced_storage {
465 my $schema = $self->schema;
466 my $write_handler = $self->schema->storage->balancer;
468 $schema->storage->read_handler($write_handler);
471 =head2 around: txn_do ($coderef)
473 Overload to the txn_do method, which is delegated to whatever the
474 L<write_handler> is set to. We overload this in order to wrap in inside a
475 L</execute_reliably> method.
479 around 'txn_do' => sub {
480 my($txn_do, $self, $coderef, @args) = @_;
481 $self->execute_reliably(sub {$self->$txn_do($coderef, @args)});
486 Check that the master and at least one of the replicants is connected.
493 $self->master->connected &&
494 $self->pool->connected_replicants;
497 =head2 ensure_connected
499 Make sure all the storages are connected.
503 sub ensure_connected {
505 foreach my $source ($self->all_storages) {
506 $source->ensure_connected(@_);
512 Set the limit_dialect for all existing storages
518 foreach my $source ($self->all_storages) {
519 $source->limit_dialect(@_);
525 Set the quote_char for all existing storages
531 foreach my $source ($self->all_storages) {
532 $source->quote_char(@_);
538 Set the name_sep for all existing storages
544 foreach my $source ($self->all_storages) {
545 $source->name_sep(@_);
551 Set the schema object for all existing storages
557 foreach my $source ($self->all_storages) {
558 $source->set_schema(@_);
564 set a debug flag across all storages
570 foreach my $source ($self->all_storages) {
577 set a debug object across all storages
583 foreach my $source ($self->all_storages) {
584 $source->debugobj(@_);
590 set a debugfh object across all storages
596 foreach my $source ($self->all_storages) {
597 $source->debugfh(@_);
603 set a debug callback across all storages
609 foreach my $source ($self->all_storages) {
610 $source->debugcb(@_);
616 disconnect everything
622 foreach my $source ($self->all_storages) {
623 $source->disconnect(@_);
629 John Napiorkowski <john.napiorkowski@takkle.com>
631 Based on code originated by:
633 Norbert Csongrádi <bert@cpan.org>
634 Peter Siklósi <einon@einon.hu>
638 You may distribute this code under the same terms as Perl itself.