leoboy has asked for the wisdom of the Perl Monks concerning the following question:

Hello,

I'm developping a tiny app with Redis (some kind of noSQL database) as storage back-end. Redis.pm (http://search.cpan.org/~melo/Redis-1.904/lib/Redis.pm) is fairly cool, however, I would like to protect Redis methods with eval{}, to be able to handle situations on which the Redis server disappears, and so on.

My first idea was to create a new class, let's say SRedis, that inherits from Redis :

package SRedis ; our @ISA = qw(Redis) ;
As I'm a lazy boy, I just want to handle all original Redis methods with an AUTOLOAD method on my SRedis package. Code looks like this :
## Wonderful catch-all ## Input : Nothing ## Output : Asked data sub AUTOLOAD { # Input variables my ($instance_ref) = shift(@_) ; # Internal vars $tries = 0 ; # Determine initially called sub my $command = $AUTOLOAD ; # Format it to call corresponding method on Redis package $command =~ s/.*:// ; $command = 'SUPER::' . $command ; TRY:while($tries < $maxtries) { # Go on $errorcode = eval {@results = $instance_ref->$command(@_)} ; # Did it went well ? if(!defined($errorcode)) { # No - I should log something here # Update count $tries++ ; # Wait a little bit ? sleep(1) ; # Go back to work next TRY; } # Otherwise that's cool last TRY; } # Return data return (@results) ; }

My problem is that I don't know what kind of data the original command may return. Could be a scalar, an array or a hash. In this piece of code, I assume that the command returns an array.

How can I handle any kind of return in the eval, and make AUTOLOAD return it correctly to the initial caller ?

Thank you in advance for your advices.

Replies are listed 'Best First'.
Re: How to protect Redis calls with eval ?
by Tux (Canon) on Apr 27, 2011 at 15:39 UTC

    Wouldn't it be way more easy to ask the author of Redis to include/implement that feature? That way all people can enjoy the extra features.

    I had few issues with Redis and they all got fixed relatively soon after I contacted him, so he has proven to be rather responsive.

    Of course offering hime a patch that implements your suggestion to the original module would probably be even more welcome :)


    Enjoy, Have FUN! H.Merijn

      Yeah, you're right, I'll submit a patch to the author to share my work.

      Actually I've written some extra code that tries to disconnect / reconnect in case of problem, and that works pretty well.

      Maybe soon in the next version of Redis.pm ;)

Re: How to protect Redis calls with eval ?
by Anonymous Monk on Apr 27, 2011 at 11:26 UTC

      Worked fine with wantarray(). Thanks for the hint. Here is the final code :

      ## Wonderful catch-all sub AUTOLOAD { # Input variables my ($instance_ref) = shift(@_) ; # Internal vars $tries = 0 ; my $wantarray = wantarray() ; # Determine initially called sub my $command = $AUTOLOAD ; # Format it to call corresponding method on Redis package $command =~ s/.*:// ; $command = 'SUPER::' . $command ; TRY:while($tries < $maxtries) { # Array $errorcode = eval {@result_array = $instance_ref->$command(@_) +} if(defined($wantarray) && $wantarray) ; # Scalar $errorcode = eval {$result_scalar = $instance_ref->$command(@_ +)} if(defined($wantarray) && !$wantarray) ; # Scalar $errorcode = eval {$instance_ref->$command(@_)} if(!defined($wantarray)) ; # Did it went well ? if(!defined($errorcode) && $@ ne '') { # No - log something ? warn("Could not send command $command to Redis server, try + $tries on $maxtries, reason : $@") ; # Update count $tries++ ; # Wait a little bit sleep(1) ; # Go back to work next TRY; } # Otherwise that's cool last TRY; } # Return data return @result_array if(defined($wantarray) && $wantarray) ; return $result_scalar if(defined($wantarray) && !$wantarray) ; return if(!defined($wantarray)) ; }

      Thanks for your help.

Re: How to protect Redis calls with eval ?
by shmem (Chancellor) on Apr 27, 2011 at 15:10 UTC

    That could work. However, if you say

    $errorcode = eval {@results = $instance_ref->$command(@_)} ;

    you are evaluating @results in scalar context and assigning the result of that evaluation to $errorcode - which is not what your want. Just say e.g.

    @results = eval { $instance_ref->$command(@_)} ;

    and check $@ for errors, which gets set if your eval'ed code dies.