[Catalyst] Graceful handling of database failure

Mitch Jackson perimus at gmail.com
Thu Jun 19 18:56:18 BST 2008


> If you are using DBIx::Class, I'd be surprised if it doesn't do the
> right thing already.

> As far as catching the error, make sure that your model throws a
> proper exception and you can catch that in the end action.
>
> Something like this is what I tend to use:
>
> sub render_end : Private ActionClass('RenderView') { }
>
> sub end : Private {
>   my ( $self, $c ) = @_;
>   $c->forward('render_end');
>   if ( @{$c->error} ) {
>      # Handle errors
>   }
> }

J,

Thanks for the help.  I tried the approach you suggested, but it's not
working for me.  Yes, I am using DBix::Class, and yes, it does seem to
do The Right Thing and somehow throw a $c->error that makes it out to
the log.

I wrote an action similar to the one you suggest ( code attached below
) and threw a bad username into the database connection string.

Watching Debug output, it appears the end action is never called when
the error happens.  The debug output looks like this:

=============================================================
You can connect to your server at http://myapp.com:3000
DBI connect('table:localhost','bad_username',...) failed: Access
denied for user 'bad_username'@'localhost' (using password: YES) at
/usr/lib/perl5/site_perl/5.8.8/DBIx/Class/Storage/DBI.pm line 839
DBI connect('table:localhost','bad_username',...) failed: Access
denied for user 'bad_username'@'localhost' (using password: YES) at
/usr/lib/perl5/site_perl/5.8.8/DBIx/Class/Storage/DBI.pm line 839
[info] *** Request 1 (0.200/s) [3553] [Thu Jun 19 12:35:54 2008] ***
[debug] "GET" request for "/" from "127.0.0.1"
[debug] Found sessionid "efa809c86e12c56089e8aaaf24e62e56ec23f7ba" in cookie
[error] DBIx::Class::ResultSet::find_or_create(): DBI Connection
failed: Access denied for user 'bad_username'@'localhost' (using
password: YES) at
/usr/lib/perl5/site_perl/5.8.8/Catalyst/Plugin/Session/Store/DBIC/Delegate.pm
line 33
[info] Request took 0.326993s (3.058/s)
.----------------------------------------------------------------+-----------.
| Action                                                         | Time      |
+----------------------------------------------------------------+-----------+
| /begin                                                         | 0.000333s |
| /index                                                         | 0.039518s |
'----------------------------------------------------------------+-----------'

[error] Caught exception in engine
"DBIx::Class::ResultSet::find_or_create(): DBI Connection failed:
Access denied for user 'bad_username'@'localhost' (using password:
YES) at /usr/lib/perl5/site_perl/5.8.8/Catalyst/Plugin/Session/Store/DBIC/Delegate.pm
line 33"
=============================================================

Since /end is never called, the error handling never happens.

Is there some other way to tie into the error handling, or am I
missing something?  Following is code I'm using for end action:

Thanks,

/Mitch

=============================================================
sub render_end : ActionClass('RenderView') {}

sub end : {
# The default error handling of Catalyst gets pre-empted here with our own,
# unless the app is operating in debug mode.

# First, we forward to the standard renderer end action.  After, we
# check for errors.  If they exist, we handle the errors and clear them from
# catalyst, but add them to the stash..  Then we override the template
# to use error500 and display an error by forwarding back to the renderview
# a second time.

    my ( $self, $c ) = @_;

$c->log->debug( 'On a DBIx::Class Connection error this statement
never happens' );

    $c->forward('render_end');
    return unless @{ $c->error };

    # If we're running under debug mode, allow default error handling
    # and stack trace.  Otherwise, override error handling
    return if $c->debug();

    # Collect and clear catalyst's error stack, creating a copy into stash
    my @errors = grep { 1 } shift @{$c->error};
    $c->stash->{errors} = \@errors;

    for my $error ( @errors ) {
        $c->log->error( $error );
    }

    # Re-process final rendering w/o the error stack
    $c->stash->{template} = '500.nonav.tt2';
    $c->detach('render_end');
}



More information about the Catalyst mailing list