[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