Author: timbo
Date: Tue Jun  3 15:21:01 2008
New Revision: 11378

Modified:
   dbi/trunk/lib/DBI/Gofer/Execute.pm

Log:
Add gofer_execute_class to list of valid config attributes.
Improve 'DBI_GOFER_RANDOM enabled' warning. Just one more informative message 
now.
Add ability to specify the err code to use in DBI_GOFER_RANDOM.
Handle lack of response object in update_stats.


Modified: dbi/trunk/lib/DBI/Gofer/Execute.pm
==============================================================================
--- dbi/trunk/lib/DBI/Gofer/Execute.pm  (original)
+++ dbi/trunk/lib/DBI/Gofer/Execute.pm  Tue Jun  3 15:21:01 2008
@@ -35,6 +35,7 @@
 # define valid configuration attributes (args to new())
 # the values here indicate the basic type of values allowed
 my %configuration_attributes = (
+    gofer_execute_class => 1,
     default_connect_dsn => 1,
     forced_connect_dsn  => 1,
     default_connect_attributes => {},
@@ -209,7 +210,7 @@
     $dbh->{ShowErrorStatement} = 1 if $local_log;
 
     # XXX should probably just be a Callbacks => arg to connect_cached
-    # with a cache of pre-built callback hoks (memoized, without $self) 
+    # with a cache of pre-built callback hooks (memoized, without $self) 
     if (my $random = $self->{forced_gofer_random} || $ENV{DBI_GOFER_RANDOM}) {
         $self->_install_rand_callbacks($dbh, $random);
     }
@@ -275,7 +276,10 @@
     DBI->trace_msg("-----> execute_request\n");
 
     my @warnings;
-    local $SIG{__WARN__} = sub { push @warnings, @_; warn @_ if $local_log };
+    local $SIG{__WARN__} = sub {
+        push @warnings, @_;
+        warn @_ if $local_log;
+    };
 
     my $response = eval {
 
@@ -564,6 +568,7 @@
 }
 
 
+# XXX would be nice to make this a generic DBI module
 sub _install_rand_callbacks {
     my ($self, $dbh, $dbi_gofer_random) = @_;
 
@@ -574,22 +579,30 @@
     return if (($prev->{_dbi_gofer_random_spec}||'') eq $dbi_gofer_random);
     $prev->{_dbi_gofer_random_spec} = $dbi_gofer_random;
 
-    my ($fail_percent, $delay_percent, $delay_duration);
+    my ($fail_percent, $fail_err, $delay_percent, $delay_duration, %spec_part, 
@spec_note);
     my @specs = split /,/, $dbi_gofer_random;
     for my $spec (@specs) {
         if ($spec =~ m/^fail=(-?[.\d]+)%?$/) {
             $fail_percent = $1;
+            $spec_part{fail} = $spec;
+            next;
+        }
+        if ($spec =~ m/^err=(-?\d+)$/) {
+            $fail_err = $1;
+            $spec_part{err} = $spec;
             next;
         }
         if ($spec =~ m/^delay([.\d]+)=(-?[.\d]+)%?$/) {
             $delay_duration = $1;
             $delay_percent  = $2;
+            $spec_part{delay} = $spec;
             next;
         }
         elsif ($spec !~ m/^(\w+|\*)$/) {
             warn "Ignored DBI_GOFER_RANDOM item '$spec' which isn't a config 
or a dbh method name";
             next;
         }
+
         my $method = $spec;
         if ($callbacks->{$method} && $callbacks->{$method} != 
$prev->{$method}) {
             warn "Callback for $method method already installed so 
DBI_GOFER_RANDOM callback not installed\n";
@@ -599,9 +612,12 @@
             warn "Ignored DBI_GOFER_RANDOM item '$spec' because not preceeded 
by 'fail=N' and/or 'delayN=N'";
             next;
         }
-        warn "DBI_GOFER_RANDOM enabled for $method() - random failures/delays 
will be generated!\n";
-        $callbacks->{$method} = $self->_mk_rand_callback($method, 
$fail_percent, $delay_percent, $delay_duration);
+
+        push @spec_note, join(",", values(%spec_part), $method);
+        $callbacks->{$method} = $self->_mk_rand_callback($method, 
$fail_percent, $delay_percent, $delay_duration, $fail_err);
     }
+    warn "DBI_GOFER_RANDOM failures/delays enabled: @spec_note\n"
+        if @spec_note;
     $dbh->{Callbacks} = $callbacks;
     $dbh->{private_gofer_rand_fail_callbacks} = $callbacks;
 }
@@ -609,11 +625,11 @@
 my %_mk_rand_callback_seqn;
 
 sub _mk_rand_callback {
-    my ($self, $method, $fail_percent, $delay_percent, $delay_duration) = @_;
+    my ($self, $method, $fail_percent, $delay_percent, $delay_duration, 
$fail_err) = @_;
     my ($fail_modrate, $delay_modrate);
     $fail_percent  ||= 0;  $fail_modrate  = int(1/(-$fail_percent )*100) if 
$fail_percent;
     $delay_percent ||= 0;  $delay_modrate = int(1/(-$delay_percent)*100) if 
$delay_percent;
-    # note that $method may be "*"
+    # note that $method may be "*" but that's not recommended or documented or 
wise
     return sub {
         my ($h) = @_;
         my $seqn = ++$_mk_rand_callback_seqn{$method};
@@ -625,14 +641,15 @@
         #warn "_mk_rand_callback($fail_percent:$fail_modrate, 
$delay_percent:$delay_modrate): seqn=$seqn fail=$fail delay=$delay";
         if ($delay) {
             my $msg = "DBI_GOFER_RANDOM delaying execution of $method() by 
$delay_duration seconds\n";
-            # Note what's happening in a trace message. If the delay percent 
is an odd
-            # number then use warn() so it's sent back to the client
-            ($delay_percent % 2 == 0) ? $h->trace_msg($msg) : warn($msg);
+            # Note what's happening in a trace message. If the delay percent 
is an even
+            # number then use warn() instead so it's sent back to the client.
+            ($delay_percent % 2 == 0) ? warn($msg) : $h->trace_msg($msg);
             select undef, undef, undef, $delay_duration; # allows floating 
point value
         }
         if ($fail) {
             undef $_; # tell DBI to not call the method
-            return $h->set_err($DBI::stderr, "fake error from $method method 
induced by DBI_GOFER_RANDOM env var ($fail_percent%)");
+            return $h->set_err($fail_err || $DBI::stderr,
+                "fake error from $method method induced by DBI_GOFER_RANDOM 
env var ($fail_percent%)");
         }
         return;
     }
@@ -646,7 +663,9 @@
         $time_received,
         $store_meta, $other_meta,
     ) = @_;
-    # we assume that $response is always a valid response object
+
+    # should always have a response object here
+    carp("No response object provided") unless $request;
 
     my $stats = $self->{stats};
     $stats->{frozen_request_max_bytes} = length($frozen_request)
@@ -672,7 +691,7 @@
             if !$frozen_response;
         my @queues =  ($stats->{recent_requests} ||= []);
         push @queues, ($stats->{recent_errors}   ||= [])
-            if $response->err;
+            if !$response or $response->err;
         for my $queue (@queues) {
             push @$queue, $recent;
             shift @$queue if @$queue > $track_recent;
@@ -825,15 +844,24 @@
 The value R can be floating point, e.g., C<fail=0.05%>.
 Negative values for R have special meaning, see below.
 
+=item err=N
+
+Sets the current failure err vaue to N (instead of the DBI's default 'standard
+err value' of 2000000000). This is useful when you want to simulate a
+specific error.
+
 =item delayN=R%
 
 Set the current random delay rate to R where R is a percentage, and set the
 current delay duration to N seconds. The values of R and N can be floating 
point,
-e.g., C<delay120=0.1%>.  Negative values for R have special meaning, see below.
+e.g., C<delay0.5=0.2%>.  Negative values for R have special meaning, see below.
+
+If R is an even number (R % 2 == 0) then a message is logged via warn() which
+will be returned to, and echoed at, the client.
 
 =item methodname
 
-Applies the current current random failure rate and random delay rate and 
duration to the named method.
+Applies the current fail, err, and delay values to the named method.
 If neither a fail nor delay have been set yet then a warning is generated.
 
 =back

Reply via email to