1 package DBIx::Class::Storage::DBI::Replicated;
4 use DBIx::Class::Storage::DBI;
5 use DBIx::Class::Storage::DBI::Replicated::Pool;
6 use DBIx::Class::Storage::DBI::Replicated::Balancer;
7 use Scalar::Util qw(blessed);
9 extends 'DBIx::Class::Storage::DBI', 'Moose::Object';
13 DBIx::Class::Storage::DBI::Replicated - ALPHA Replicated database support
17 The Following example shows how to change an existing $schema to a replicated
18 storage type, add some replicated (readonly) databases, and perform reporting
21 ## Change storage_type in your schema class
22 $schema->storage_type( ['::DBI::Replicated', {balancer=>'::Random'}] );
24 ## Add some slaves. Basically this is an array of arrayrefs, where each
25 ## arrayref is database connect information
27 $schema->storage->connect_replicants(
28 [$dsn1, $user, $pass, \%opts],
29 [$dsn1, $user, $pass, \%opts],
30 [$dsn1, $user, $pass, \%opts],
35 Warning: This class is marked ALPHA. We are using this in development and have
36 some basic test coverage but the code hasn't yet been stressed by a variety
37 of databases. Individual DB's may have quirks we are not aware of. Please
38 use this in development and pass along your experiences/bug fixes.
40 This class implements replicated data store for DBI. Currently you can define
41 one master and numerous slave database connections. All write-type queries
42 (INSERT, UPDATE, DELETE and even LAST_INSERT_ID) are routed to master
43 database, all read-type queries (SELECTs) go to the slave database.
45 Basically, any method request that L<DBIx::Class::Storage::DBI> would normally
46 handle gets delegated to one of the two attributes: L</master_storage> or to
47 L</current_replicant_storage>. Additionally, some methods need to be distributed
48 to all existing storages. This way our storage class is a drop in replacement
49 for L<DBIx::Class::Storage::DBI>.
51 Read traffic is spread across the replicants (slaves) occuring to a user
52 selected algorithm. The default algorithm is random weighted.
54 TODO more details about the algorithm.
58 This class defines the following attributes.
62 Contains the classname which will instantiate the L</pool> object. Defaults
63 to: L<DBIx::Class::Storage::DBI::Replicated::Pool>.
72 'create_pool' => 'new',
78 Contains a hashref of initialized information to pass to the Balancer object.
79 See L<DBIx::Class::Storage::Replicated::Pool> for available arguments.
94 The replication pool requires a balance class to provider the methods for
95 choose how to spread the query load across each replicant in the pool.
99 has 'balancer_type' => (
104 'create_balancer' => 'new',
110 Contains a hashref of initialized information to pass to the Balancer object.
111 See L<DBIx::Class::Storage::Replicated::Balancer> for available arguments.
115 has 'balancer_args' => (
125 Is a <DBIx::Class::Storage::DBI::Replicated::Pool> or derived class. This is a
126 container class for one or more replicated databases.
132 isa=>'DBIx::Class::Storage::DBI::Replicated::Pool',
143 Is a <DBIx::Class::Storage::DBI::Replicated::Balancer> or derived class. This
144 is a class that takes a pool (<DBIx::Class::Storage::DBI::Replicated::Pool>)
150 isa=>'DBIx::Class::Storage::DBI::Replicated::Balancer',
152 handles=>[qw/auto_validate_every/],
157 The master defines the canonical state for a pool of connected databases. All
158 the replicants are expected to match this databases state. Thus, in a classic
159 Master / Slaves distributed system, all the slaves are expected to replicate
160 the Master's state as quick as possible. This is the only database in the
161 pool of databases that is allowed to handle write traffic.
167 isa=>'DBIx::Class::Storage::DBI',
171 =head1 ATTRIBUTES IMPLEMENTING THE DBIx::Storage::DBI INTERFACE
173 The following methods are delegated all the methods required for the
174 L<DBIx::Class::Storage::DBI> interface.
178 Defines an object that implements the read side of L<BIx::Class::Storage::DBI>.
182 has 'read_handler' => (
195 Defines an object that implements the write side of L<BIx::Class::Storage::DBI>.
199 has 'write_handler' => (
212 deployment_statements
232 This class defines the following methods.
236 L<DBIx::Class::Schema> when instantiating it's storage passed itself as the
237 first argument. We need to invoke L</new> on the underlying parent class, make
238 sure we properly give it a L<Moose> meta class, and then correctly instantiate
239 our attributes. Basically we pass on whatever the schema has in it's class
240 data for 'storage_type_args' to our replicated storage type.
245 my $class = shift @_;
246 my $schema = shift @_;
247 my $storage_type_args = shift @_;
248 my $obj = $class->SUPER::new($schema, $storage_type_args, @_);
250 ## Hate to do it this way, but can't seem to get advice on the attribute working right
251 ## maybe we can do a type and coercion for it.
252 if( $storage_type_args->{balancer_type} && $storage_type_args->{balancer_type}=~m/^::/) {
253 $storage_type_args->{balancer_type} = 'DBIx::Class::Storage::DBI::Replicated::Balancer'.$storage_type_args->{balancer_type};
254 eval "require $storage_type_args->{balancer_type}";
257 return $class->meta->new_object(
258 __INSTANCE__ => $obj,
266 Lazy builder for the L</master> attribute.
271 DBIx::Class::Storage::DBI->new;
274 =head2 _build_pool_type
276 Lazy builder for the L</pool_type> attribute.
280 sub _build_pool_type {
281 return 'DBIx::Class::Storage::DBI::Replicated::Pool';
286 Lazy builder for the L</pool> attribute.
292 $self->create_pool(%{$self->pool_args});
295 =head2 _build_balancer_type
297 Lazy builder for the L</balancer_type> attribute.
301 sub _build_balancer_type {
302 return 'DBIx::Class::Storage::DBI::Replicated::Balancer::First';
305 =head2 _build_balancer
307 Lazy builder for the L</balancer> attribute. This takes a Pool object so that
308 the balancer knows which pool it's balancing.
312 sub _build_balancer {
314 $self->create_balancer(
316 master=>$self->master,
317 %{$self->balancer_args},);
320 =head2 _build_write_handler
322 Lazy builder for the L</write_handler> attribute. The default is to set this to
327 sub _build_write_handler {
328 return shift->master;
331 =head2 _build_read_handler
333 Lazy builder for the L</read_handler> attribute. The default is to set this to
338 sub _build_read_handler {
339 return shift->balancer;
342 =head2 around: connect_replicants
344 All calls to connect_replicants needs to have an existing $schema tacked onto
345 top of the args, since L<DBIx::Storage::DBI> needs it.
349 around 'connect_replicants' => sub {
350 my ($method, $self, @args) = @_;
351 $self->$method($self->schema, @args);
356 Returns an array of of all the connected storage backends. The first element
357 in the returned array is the master, and the remainings are each of the
365 return grep {defined $_ && blessed $_} (
371 =head2 set_reliable_storage
373 Sets the current $schema to be 'reliable', that is all queries, both read and
374 write are sent to the master
378 sub set_reliable_storage {
380 my $schema = $self->schema;
381 my $write_handler = $self->schema->storage->write_handler;
383 $schema->storage->read_handler($write_handler);
386 =head2 set_balanced_storage
388 Sets the current $schema to be use the </balancer> for all reads, while all
389 writea are sent to the master only
393 sub set_balanced_storage {
395 my $schema = $self->schema;
396 my $write_handler = $self->schema->storage->balancer;
398 $schema->storage->read_handler($write_handler);
403 Check that the master and at least one of the replicants is connected.
411 $self->master->connected &&
412 $self->pool->connected_replicants;
415 =head2 ensure_connected
417 Make sure all the storages are connected.
421 sub ensure_connected {
423 foreach my $source ($self->all_storages) {
424 $source->ensure_connected(@_);
430 Set the limit_dialect for all existing storages
436 foreach my $source ($self->all_storages) {
437 $source->limit_dialect(@_);
443 Set the quote_char for all existing storages
449 foreach my $source ($self->all_storages) {
450 $source->quote_char(@_);
456 Set the name_sep for all existing storages
462 foreach my $source ($self->all_storages) {
463 $source->name_sep(@_);
469 Set the schema object for all existing storages
475 foreach my $source ($self->all_storages) {
476 $source->set_schema(@_);
482 set a debug flag across all storages
488 foreach my $source ($self->all_storages) {
495 set a debug object across all storages
501 foreach my $source ($self->all_storages) {
502 $source->debugobj(@_);
508 set a debugfh object across all storages
514 foreach my $source ($self->all_storages) {
515 $source->debugfh(@_);
521 set a debug callback across all storages
527 foreach my $source ($self->all_storages) {
528 $source->debugcb(@_);
534 disconnect everything
540 foreach my $source ($self->all_storages) {
541 $source->disconnect(@_);
547 Norbert Csongrádi <bert@cpan.org>
549 Peter Siklósi <einon@einon.hu>
551 John Napiorkowski <john.napiorkowski@takkle.com>
555 You may distribute this code under the same terms as Perl itself.