use Moose;
use MooseX::AttributeHelpers;
use DBIx::Class::Storage::DBI::Replicated::Replicant;
-use List::Util qw(sum);
+use List::Util 'sum';
+use Scalar::Util 'reftype';
+use Carp::Clan qw/^DBIx::Class/;
+use MooseX::Types::Moose qw/Num Int ClassName HashRef/;
+
+use namespace::clean -except => 'meta';
=head1 NAME
has 'maximum_lag' => (
is=>'rw',
- isa=>'Num',
+ isa=>Num,
required=>1,
lazy=>1,
default=>0,
has 'last_validated' => (
is=>'rw',
- isa=>'Int',
+ isa=>Int,
reader=>'last_validated',
writer=>'_last_validated',
lazy=>1,
has 'replicant_type' => (
is=>'ro',
- isa=>'ClassName',
+ isa=>ClassName,
required=>1,
default=>'DBIx::Class::Storage::DBI',
handles=>{
has 'replicants' => (
is=>'rw',
metaclass => 'Collection::Hash',
- isa=>'HashRef[DBIx::Class::Storage::DBI]',
+ isa=>HashRef['DBIx::Class::Storage::DBI'],
default=>sub {{}},
provides => {
'set' => 'set_replicant',
my @newly_created = ();
foreach my $connect_info (@_) {
+ $connect_info = [ $connect_info ]
+ if reftype $connect_info ne 'ARRAY';
+
+ croak "coderef replicant connect_info not supported"
+ if ref $connect_info->[0] && reftype $connect_info->[0] eq 'CODE';
+
my $replicant = $self->connect_replicant($schema, $connect_info);
- my ($key) = ($connect_info->[0]=~m/^dbi\:.+\:(.+)$/);
+
+ my $key = $connect_info->[0];
+ $key = $key->{dsn} if ref $key && reftype $key eq 'HASH';
+ ($key) = ($key =~ m/^dbi\:.+\:(.+)$/);
+
$self->set_replicant( $key => $replicant);
push @newly_created, $replicant;
}
sub connect_replicant {
my ($self, $schema, $connect_info) = @_;
my $replicant = $self->create_replicant($schema);
-
- $replicant->connect_info($connect_info);
- $replicant->ensure_connected;
- DBIx::Class::Storage::DBI::Replicated::Replicant->meta->apply($replicant);
-
+ $replicant->connect_info($connect_info);
+ $self->_safely_ensure_connected($replicant);
+ DBIx::Class::Storage::DBI::Replicated::Replicant->meta->apply($replicant);
return $replicant;
}
+=head2 _safely_ensure_connected ($replicant)
+
+The standard ensure_connected method with throw an exception should it fail to
+connect. For the master database this is desirable, but since replicants are
+allowed to fail, this behavior is not desirable. This method wraps the call
+to ensure_connected in an eval in order to catch any generated errors. That
+way a slave to go completely offline (ie, the box itself can die) without
+bringing down your entire pool of databases.
+
+=cut
+
+sub _safely_ensure_connected {
+ my ($self, $replicant, @args) = @_;
+ eval {
+ $replicant->ensure_connected(@args);
+ };
+ if ($@) {
+ $replicant
+ ->debugobj
+ ->print(
+ sprintf( "Exception trying to ->ensure_connected for replicant %s, error is %s",
+ $replicant->_dbi_connect_info->[0], $@)
+ );
+ return;
+ }
+ return 1;
+}
+
=head2 connected_replicants
Returns true if there are connected replicants. Actually is overloaded to
Calling this method will generate queries on the replicant databases so it is
not recommended that you run them very often.
+This method requires that your underlying storage engine supports some sort of
+native replication mechanism. Currently only MySQL native replication is
+supported. Your patches to make other replication types work are welcomed.
+
=cut
sub validate_replicants {
my $self = shift @_;
foreach my $replicant($self->all_replicants) {
- if(
- $replicant->is_replicating &&
- $replicant->lag_behind_master <= $self->maximum_lag &&
- $replicant->ensure_connected
- ) {
- $replicant->active(1)
+ if($self->_safely_ensure_connected($replicant)) {
+ my $is_replicating = $replicant->is_replicating;
+ unless(defined $is_replicating) {
+ $replicant->debugobj->print("Storage Driver ".ref($self)." Does not support the 'is_replicating' method. Assuming you are manually managing.\n");
+ next;
+ } else {
+ if($is_replicating) {
+ my $lag_behind_master = $replicant->lag_behind_master;
+ unless(defined $lag_behind_master) {
+ $replicant->debugobj->print("Storage Driver ".ref($self)." Does not support the 'lag_behind_master' method. Assuming you are manually managing.\n");
+ next;
+ } else {
+ if($lag_behind_master <= $self->maximum_lag) {
+ $replicant->active(1);
+ } else {
+ $replicant->active(0);
+ }
+ }
+ } else {
+ $replicant->active(0);
+ }
+ }
} else {
$replicant->active(0);
}
}
## Mark that we completed this validation.
- $self->_last_validated(time);
+ $self->_last_validated(time);
}
=head1 AUTHOR