Replicated - fixup types and namespace::clean
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Replicated / Balancer.pm
index ed7007d..798c0ef 100644 (file)
@@ -2,10 +2,13 @@ package DBIx::Class::Storage::DBI::Replicated::Balancer;
 
 use Moose::Role;
 requires 'next_storage';
+use MooseX::Types::Moose qw/Int/;
+
+use namespace::clean -except => 'meta';
 
 =head1 NAME
 
-DBIx::Class::Storage::DBI::Replicated::Balancer; A Software Load Balancer 
+DBIx::Class::Storage::DBI::Replicated::Balancer - A Software Load Balancer 
 
 =head1 SYNOPSIS
 
@@ -30,9 +33,9 @@ validating every query.
 =cut
 
 has 'auto_validate_every' => (
-    is=>'rw',
-    isa=>'Int',
-    predicate=>'has_auto_validate_every',
+  is=>'rw',
+  isa=>Int,
+  predicate=>'has_auto_validate_every',
 );
 
 =head2 master
@@ -44,9 +47,9 @@ ultimate fallback.
 =cut
 
 has 'master' => (
-    is=>'ro',
-    isa=>'DBIx::Class::Storage::DBI',
-    required=>1,
+  is=>'ro',
+  isa=>'DBIx::Class::Storage::DBI',
+  required=>1,
 );
 
 =head2 pool
@@ -57,9 +60,9 @@ balance.
 =cut
 
 has 'pool' => (
-    is=>'ro',
-    isa=>'DBIx::Class::Storage::DBI::Replicated::Pool',
-    required=>1,
+  is=>'ro',
+  isa=>'DBIx::Class::Storage::DBI::Replicated::Pool',
+  required=>1,
 );
 
 =head2 current_replicant
@@ -76,14 +79,14 @@ via it's balancer object.
 =cut
 
 has 'current_replicant' => (
-    is=> 'rw',
-    isa=>'DBIx::Class::Storage::DBI',
-    lazy_build=>1,
-    handles=>[qw/
-        select
-        select_single
-        columns_info_for
-    /],
+  is=> 'rw',
+  isa=>'DBIx::Class::Storage::DBI',
+  lazy_build=>1,
+  handles=>[qw/
+    select
+    select_single
+    columns_info_for
+  /],
 );
 
 =head1 METHODS
@@ -97,8 +100,8 @@ Lazy builder for the L</current_replicant_storage> attribute.
 =cut
 
 sub _build_current_replicant {
-    my $self = shift @_;
-    $self->next_storage;
+  my $self = shift @_;
+  $self->next_storage;
 }
 
 =head2 next_storage
@@ -124,23 +127,39 @@ or just just forgot to create them :)
 =cut
 
 around 'next_storage' => sub {
-       my ($next_storage, $self, @args) = @_;
-       my $now = time;
-       
-    ## Do we need to validate the replicants?
-    if(
-       $self->has_auto_validate_every && 
-       ($self->auto_validate_every + $self->pool->last_validated) <= $now
-    ) {
-        $self->pool->validate_replicants;
-    }
-    
-    ## Get a replicant, or the master if none
-    my $next = $self->$next_storage(@args);
-    return $next ? $next:$self->master;        
+  my ($next_storage, $self, @args) = @_;
+  my $now = time;
+
+  ## Do we need to validate the replicants?
+  if(
+     $self->has_auto_validate_every && 
+     ($self->auto_validate_every + $self->pool->last_validated) <= $now
+  ) {   
+      $self->pool->validate_replicants;
+  }
+
+  ## Get a replicant, or the master if none
+  if(my $next = $self->$next_storage(@args)) {
+    return $next;
+  } else {
+    $self->master->debugobj->print("No Replicants validate, falling back to master reads. ");
+    return $self->master;
+  }
 };
 
-=head2 before: select
+=head2 increment_storage
+
+Rolls the Storage to whatever is next in the queue, as defined by the Balancer.
+
+=cut
+
+sub increment_storage {
+  my $self = shift @_;
+  my $next_replicant = $self->next_storage;
+  $self->current_replicant($next_replicant);
+}
+
+=head2 around: select
 
 Advice on the select attribute.  Each time we use a replicant
 we need to change it via the storage pool algorithm.  That way we are spreading
@@ -148,13 +167,19 @@ the load evenly (hopefully) across existing capacity.
 
 =cut
 
-before 'select' => sub {
-    my $self = shift @_;
-    my $next_replicant = $self->next_storage;
-    $self->current_replicant($next_replicant);
+around 'select' => sub {
+  my ($select, $self, @args) = @_;
+  
+  if (my $forced_pool = $args[-1]->{force_pool}) {
+    delete $args[-1]->{force_pool};
+    return $self->_get_forced_pool($forced_pool)->select(@args); 
+  } else {
+    $self->increment_storage;
+    return $self->$select(@args);
+  }
 };
 
-=head2 before: select_single
+=head2 around: select_single
 
 Advice on the select_single attribute.  Each time we use a replicant
 we need to change it via the storage pool algorithm.  That way we are spreading
@@ -162,10 +187,16 @@ the load evenly (hopefully) across existing capacity.
 
 =cut
 
-before 'select_single' => sub {
-    my $self = shift @_;
-    my $next_replicant = $self->next_storage;
-    $self->current_replicant($next_replicant);
+around 'select_single' => sub {
+  my ($select_single, $self, @args) = @_;
+  
+  if (my $forced_pool = $args[-1]->{force_pool}) {
+    delete $args[-1]->{force_pool};
+    return $self->_get_forced_pool($forced_pool)->select_single(@args); 
+  } else {
+    $self->increment_storage;
+    return $self->$select_single(@args);
+  }
 };
 
 =head2 before: columns_info_for
@@ -177,11 +208,29 @@ the load evenly (hopefully) across existing capacity.
 =cut
 
 before 'columns_info_for' => sub {
-    my $self = shift @_;
-    my $next_replicant = $self->next_storage;
-    $self->current_replicant($next_replicant);
+  my $self = shift @_;
+  $self->increment_storage;
 };
 
+=head2 _get_forced_pool ($name)
+
+Given an identifier, find the most correct storage object to handle the query.
+
+=cut
+
+sub _get_forced_pool {
+  my ($self, $forced_pool) = @_;
+  if(blessed $forced_pool) {
+    return $forced_pool;
+  } elsif($forced_pool eq 'master') {
+    return $self->master;
+  } elsif(my $replicant = $self->pool->replicants($forced_pool)) {
+    return $replicant;
+  } else {
+    $self->master->throw_exception("$forced_pool is not a named replicant.");
+  }   
+}
+
 =head1 AUTHOR
 
 John Napiorkowski <john.napiorkowski@takkle.com>