Notes génériques
J'ai rencontré le même problème dernièrement avec plusieurs conditions à remplir :
- la solution doit être sûre pour les threads
- plusieurs connexions à la base de données à partir de la même machine peuvent être actives en même temps, ce qui a pour effet de tuer la seule connexion/requête.
- l'application contient des connexions à de nombreuses bases de données différentes - un gestionnaire portable pour chaque hôte de base de données
Nous avions la disposition de classe suivante ( Malheureusement, je ne peux pas publier de sources réelles. ) :
class AbstractModel: pass
class FirstDatabaseModel(AbstractModel): pass # Connection to one DB host
class SecondDatabaseModel(AbstractModel): pass # Connection to one DB host
Et a créé plusieurs fils de discussion pour chaque modèle.
Solution Python 3.2
Dans notre application un modèle = une base de données . J'ai donc créé " raccordement au service "pour chaque modèle (de sorte que nous puissions exécuter KILL
en connexion parallèle). Par conséquent, si une instance de FirstDatabaseModel
a été créée, 2 connexions à la base de données ont été créées ; si 5 instances ont été créées, 6 connexions ont été utilisées :
class AbstractModel:
_service_connection = None # Formal declaration
def __init__(self):
''' Somehow load config and create connection
'''
self.config = # ...
self.connection = MySQLFromConfig(self.config)
self._init_service_connection()
# Get connection ID (pseudocode)
self.connection_id = self.connection.FetchOneCol('SELECT CONNECTION_ID()')
def _init_service_connection(self):
''' Initialize one singleton connection for model
'''
cls = type(self)
if cls._service_connection is not None:
return
cls._service_connection = MySQLFromConfig(self.config)
Il nous faut maintenant un tueur :
def _kill_connection(self):
# Add your own mysql data escaping
sql = 'KILL CONNECTION {}'.format(self.connection_id)
# Do your own connection check and renewal
type(self)._service_connection.execute(sql)
Remarque : connection.execute
= créer un curseur, exécuter, fermer le curseur.
Et sécuriser le thread killer en utilisant threading.Lock
:
def _init_service_connection(self):
''' Initialize one singleton connection for model
'''
cls = type(self)
if cls._service_connection is not None:
return
cls._service_connection = MySQLFromConfig(self.config)
cls._service_connection_lock = threading.Lock()
def _kill_connection(self):
# Add your own mysql data escaping
sql = 'KILL CONNECTION {}'.format(self.connection_id)
cls = type(self)
# Do your own connection check and renewal
try:
cls._service_connection_lock.acquire()
cls._service_connection.execute(sql)
finally:
cls._service_connection_lock.release()
Enfin, ajoutez la méthode d'exécution temporelle en utilisant threading.Timer
:
def timed_query(self, sql, timeout=5):
kill_query_timer = threading.Timer(timeout, self._kill_connection)
kill_query_timer.start()
try:
self.connection.long_query()
finally:
kill_query_timer.cancel()