3 See the NOTICE file distributed with
this work
for additional information
4 regarding copyright ownership.
6 Licensed under the Apache License, Version 2.0 (the
"License");
7 you may not use
this file except in compliance with the License.
8 You may obtain a copy of the License at
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an
"AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License
for the specific language governing permissions and
16 limitations under the License.
23 Please email comments or questions to the
public Ensembl
24 developers list at <http:
26 Questions may also be sent to the Ensembl help desk at
34 for one backing connection to be used
for multiple DBs
42 # With a 10 minute timeout reconnection in milliseconds
47 This
class is used to maintain one active connection to a database whilst it
48 appears to be working against multiple schemas. It does this by checking the
49 currently connected database before performing any query which could require
50 a database change such as prepare.
52 This class is only intended for internal use so please do not use unless
53 you are aware of what it will do and what the consequences of its
usage are.
59 package Bio::EnsEMBL::DBSQL::ProxyDBConnection;
70 use Time::HiRes qw/time/;
73 my ($class, @args) = @_;
74 my ($dbc, $dbname, $reconnect_interval) = rearrange([qw/DBC DBNAME RECONNECT_INTERVAL/], @args);
75 throw "No DBConnection -DBC given" unless $dbc;
76 throw "No database name -DBNAME given" unless $dbname;
77 my $self = $class->SUPER::new($dbc);
78 $self->dbname($dbname);
79 if($reconnect_interval) {
80 $self->reconnect_interval($reconnect_interval);
86 =head2 switch_database
88 Description : Performs a
switch of the backing
DBConnection if the currently
89 connected database is not the same as the database
this proxy
90 wants to connect to. It currently supports MySQL, Oracle and
91 Postges switches is untested with all bar MySQL. If it
92 cannot
do a live DB/schema
switch then it will disconnect
93 the connection and then wait
for the next process to
94 connect therefore switching the DB.
95 Exceptions : None but will warn
if you attempt to
switch a DB with
96 active kids attached to the proxied database handle.
100 sub switch_database {
102 my $proxy = $self->__proxy();
103 my $backing_dbname = $proxy->
dbname();
104 my $dbname = $self->dbname();
107 if(defined $dbname) {
108 if(defined $backing_dbname) {
109 $switch = ($dbname ne $backing_dbname) ? 1 : 0;
116 $switch = 1
if defined $backing_dbname;
120 $proxy->dbname($dbname);
121 if($proxy->connected()) {
122 my $kids = $proxy->db_handle()->{Kids};
123 my $driver = lc($proxy->driver());
124 #Edit to add other DB switching strategies on a per driver basis
125 if($driver eq
'mysql') {
126 $proxy->do(
'use '.$dbname);
128 elsif($driver eq
'oracle') {
129 $proxy->do(
'ALTER SESSION SET CURRENT_SCHEMA = '.$dbname);
131 elsif($driver eq
'pg') {
132 $proxy->do(
'set search_path to '.$dbname);
136 warning
"Attempting a database switch from '$backing_dbname' to '$dbname' with $kids active handle(s). Check your logic or do not use a ProxyDBConnection";
138 $proxy->disconnect_if_idle();
146 =head2 check_reconnection
148 Description : Looks to see
if the last time we used the backing DBI
149 connection was greater than the reconnect_interval()
150 provided at construction or runtime. If enought time has
151 elapsed then a reconnection is attempted. We do not
152 attempt a reconnection if:
154 - No reconnect_interval was set
155 - The connection was not active
157 Exceptions : None apart from those raised from the reconnect() method
161 sub check_reconnection {
163 #Return early if we had no reconnection interval
164 return unless $self->{reconnect_interval};
166 my $proxy = $self->__proxy();
168 #Only attempt it if we were connected; otherwise we can just skip
169 if($proxy->connected()) {
170 if($self->_require_reconnect()) {
178 # Each time this is called we record the current time in seconds
179 # to be used by the _require_reconnect() method
182 $self->{_last_used} = int(time()*1000);
186 # Uses the _last_used() time and the current reconnect_interval() to decide
187 # if the connection has been unused for long enough that we should attempt
189 sub _require_reconnect {
191 my $interval = $self->reconnect_interval();
192 return unless $interval;
193 my $last_used = $self->{_last_used};
194 my $time_elapsed = int(time()*1000) - $last_used;
195 return $time_elapsed > $interval ? 1 : 0;
198 =head2 reconnect_interval
200 Arg[1] : Integer reconnection interval in milliseconds
201 Description : Accessor
for the reconnection interval expressed in milliseconds
202 Returntype : Int miliseconds
for a reconnection interval
206 sub reconnect_interval {
207 my ($self, $reconnect_interval) = @_;
208 $self->{
'reconnect_interval'} = $reconnect_interval
if defined $reconnect_interval;
209 return $self->{
'reconnect_interval'};
214 Arg[1] : String DB name
215 Description : Accessor
for the name of the database we should use whenever
216 a DBConnection request is made via
this class
217 Returntype : String the name of the database which we should use
223 my ($self, $dbname) = @_;
224 $self->{
'dbname'} = $dbname
if defined $dbname;
225 return $self->{
'dbname'};
228 my %SWITCH_METHODS =
map { $_ => 1 } qw/
238 # Manual override of the SqlHelper accessor to ensure it always gets the Proxy
241 if(! exists $self->{_sql_helper}) {
243 $self->{_sql_helper} = $helper;
245 return $self->{_sql_helper};
249 my ($self, $package, $method) = @_;
250 if($self->__proxy()->can($method)) {
251 if($SWITCH_METHODS{$method}) {
253 my ($local_self, @args) = @_;
254 $local_self->check_reconnection();
255 $local_self->switch_database();
256 $local_self->_last_used();
257 return $local_self->__proxy()->$method(@args);
262 my ($local_self, @args) = @_;
263 return $local_self->__proxy()->$method(@args);