Skip to content

Instantly share code, notes, and snippets.

@ulizama
Created July 10, 2012 20:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ulizama/3085991 to your computer and use it in GitHub Desktop.
Save ulizama/3085991 to your computer and use it in GitHub Desktop.
package Project::Plugin::Dancer::Rose::Database;
use strict;
use Dancer::Plugin;
use Referdia::DB;
# This module is based on the Dancer::Plugins::Database module
our $VERSION = '0.01';
my %handles;
# Hashref used as key for default handle, so we don't have a magic value that
# the user could use for one of their connection names and cause problems
# (Kudos to Igor Bujna for the idea)
my $def_handle = {};
register database => sub {
my $arg = shift;
my $handle_key = $def_handle;
my $handle;
# To be fork safe and thread safe, use a combination of the PID and TID (if
# running with use threads) to make sure no two processes/threads share
# handles. Implementation based on DBIx::Connector by David E. Wheeler.
my $pid_tid = $$;
$pid_tid .= '_' . threads->tid if $INC{'threads.pm'};
# OK, see if we have a matching handle
$handle = $handles{$pid_tid}{$handle_key} || {};
if( $handle->{rose} ){
if( $handle->{rose}->dbh->{Active} && time - $handle->{last_connection_check} < 30 ){
return $handle->{rose};
}
else{
if( _check_connection($handle->{rose}) ){
$handle->{last_connection_check} = time;
return $handle->{rose};
}
else{
Dancer::Logger::debug(
"Database connection went away, reconnecting"
);
Dancer::Factory::Hook->instance->execute_hooks(
'database_connection_lost', $handle->{rose}->dbh
);
if( $handle->{rose} ) { eval { $handle->{rose}->dbh->disconnect } }
return $handle->{rose} = _get_connection();
}
}
}
else{
# Get a new connection
if ($handle->{rose} = _get_connection()) {
$handle->{last_connection_check} = time;
$handles{$pid_tid}{$handle_key} = $handle;
if (ref $handle_key && ref $handle_key ne ref $def_handle) {
# We were given a hashref of connection settings. Shove a
# reference to that hashref into the handle, so that the hashref
# doesn't go out of scope for the life of the handle.
# Otherwise, that area of memory could be re-used, and, given
# different DB settings in a hashref that just happens to have
# the same address, we'll happily hand back the original handle.
# See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=665221
# Thanks to Sam Kington for suggesting this fix :)
$handle->{_orig_settings_hashref} = $handle_key;
}
return $handle->{rose};
} else {
return;
}
}
};
Dancer::Factory::Hook->instance->install_hooks(
qw(
database_connected
database_connection_lost
database_connection_failed
database_error
)
);
register_plugin;
# Given the settings to use, try to get a database connection
sub _get_connection {
my $rose = Referdia::DB->new;
if( !$rose->dbh ){
Dancer::Logger::error(
"Database connection failed - " . $rose->error
);
Dancer::Factory::Hook->instance->execute_hooks(
'database_connection_failed'
);
return;
}
Dancer::Logger::info('Database Connected');
Dancer::Factory::Hook->instance->execute_hooks('database_connected', $rose);
return $rose;
}
# Check the connection is alive
sub _check_connection {
my $rose = shift;
return unless $rose;
if ($rose->dbh->{Active} && (my $result = $rose->dbh->ping)) {
if (int($result)) {
# DB driver itself claims all is OK, trust it:
return 1;
} else {
# It was "0 but true", meaning the default DBI ping implementation
# Implement our own basic check, by performing a real simple query.
my $ok;
eval {
$ok = $rose->dbh->do('select 1');
};
return $ok;
}
} else {
return;
}
}
1;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment