DBConnection.pm 4.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
=pod 

=head1 NAME

    Bio::EnsEMBL::Hive::DBSQL::DBConnection

=head1 SYNOPSIS

    my $url = $dbc->url();

=head1 DESCRIPTION

13
    Extends the functionality of Bio::EnsEMBL::Hive::DBSQL::CoreDBConnection with things needed by the Hive
14

15 16
=head1 LICENSE

17
    Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
18 19 20 21 22 23 24 25 26 27

    Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

         http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software distributed under the License
    is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and limitations under the License.

28 29
=head1 CONTACT

30
    Please subscribe to the Hive mailing list:  http://listserver.ebi.ac.uk/mailman/listinfo/ehive-users  to discuss Hive-related questions or to be notified of our updates
31 32 33

=cut

34

35 36 37 38 39
package Bio::EnsEMBL::Hive::DBSQL::DBConnection;

use strict;
use warnings;

40
use Time::HiRes ('usleep');
41 42
use Bio::EnsEMBL::Hive::Utils::URL;

43
use base ('Bio::EnsEMBL::Hive::DBSQL::CoreDBConnection');
44 45


46 47
sub new {
    my $class = shift;
48
    my %flags = @_;
49

50
    if(my $url = $flags{'-url'}) {
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
        if(my $parsed_url = Bio::EnsEMBL::Hive::Utils::URL::parse( $url )) {

            return $class->SUPER::new(
                -driver => $parsed_url->{'driver'},
                -host   => $parsed_url->{'host'},
                -port   => $parsed_url->{'port'},
                -user   => $parsed_url->{'user'},
                -pass   => $parsed_url->{'pass'},
                -dbname => $parsed_url->{'dbname'},
                -disconnect_when_inactive       => $parsed_url->{'conn_params'}->{'discon'},
                -reconnect_when_connection_lost => $parsed_url->{'conn_params'}->{'recon'},
            );

        } else {
            die "Could not create DBC because could not parse the URL '$url'";
        }
    } else {
        return $class->SUPER::new( @_ );
    }
}

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
=head2 url

    Arg [1]    : String $environment_variable_name_to_store_password_in (optional)
    Example    : $url = $dbc->url;
    Description: Constructs a URL string for this database connection.
    Returntype : string of format  mysql://<user>:<pass>@<host>:<port>/<dbname>
                               or  sqlite:///<dbname>
    Exceptions : none
    Caller     : general

=cut

sub url {
    my ($self, $psw_env_var_name) = @_;

    my $url = $self->driver . '://';

    if($self->username) {
        $url .= $self->username;

        if(my $psw_expression = $self->password) {
            if($psw_env_var_name) {
                $ENV{$psw_env_var_name} = $psw_expression;
                $psw_expression = '${'.$psw_env_var_name.'}';
            }
            $url .= ':'.$psw_expression if($psw_expression);
        }

        $url .= '@';
    }

    if($self->host) {
        $url .= $self->host;

        if($self->port) {
            $url .= ':'.$self->port;
        }
    }
    $url .= '/' . $self->dbname;

    return $url;
}


116 117
sub protected_prepare_execute {     # try to resolve certain mysql "Deadlocks" by trying again (a useful workaround even in mysql 5.1.61)
    my $self        = shift @_;
118 119 120
    my $sql_params  = shift @_;

    my $sql_cmd     = shift @$sql_params;
121

122 123
    my $attempts        = 4;
    my $sleep_max_sec   = 1;
124
    my $log_message_adaptor;
125

Leo Gordon's avatar
Leo Gordon committed
126 127
    my $retval;

128
    foreach my $attempt (1..$attempts) {
129
        eval {
130 131
            my $sth = $self->prepare( $sql_cmd );
            $retval = $sth->execute( @$sql_params );
132 133 134 135
            $sth->finish;
            1;
        } or do {
            if($@ =~ /Deadlock found when trying to get lock; try restarting transaction/) {    # ignore this particular error
136 137 138 139 140 141 142 143 144 145

                unless($log_message_adaptor) {
                    require Bio::EnsEMBL::Hive::DBSQL::DBAdaptor;
                    my $slave_dba = Bio::EnsEMBL::Hive::DBSQL::DBAdaptor->new(
                        -dbconn => $self,
                        -no_sql_schema_version_check => 1,
                    );
                    $log_message_adaptor = $slave_dba->get_LogMessageAdaptor();
                }

146
                my $this_sleep_sec = rand( $sleep_max_sec );
147
                $log_message_adaptor->store_hive_message( "Caught a DEADLOCK when trying to execute '$sql_cmd' (attempt #$attempt), retrying in $this_sleep_sec sec", 0 );
148 149 150

                usleep( $this_sleep_sec*1000000 );
                $sleep_max_sec *= 2;
151 152 153 154 155 156
                next;
            }
            die $@;     # but definitely report other errors
        };
        last;
    }
157
    die "After $attempts attempts the query '$sql_cmd' is still in a deadlock: $@" if($@);
Leo Gordon's avatar
Leo Gordon committed
158 159

    return $retval;
160 161
}

162 163
1;