python MySQLDB délai d'expiration de requête
Je suis en train de faire respecter une limite de temps sur des requêtes en python MySQLDB. J'ai une situation où je n'ai pas de contrôle sur les requêtes, mais il faut s'assurer qu'ils ne courent pas sur une limite de temps. J'ai essayé d'utiliser le signal.SIGALRM pour interrompre l'appel à l'exécution, mais cela ne semble pas fonctionner. Le signal est envoyé, mais n'a pas pris jusqu'à après l'appel pour exécuter les finitions.
J'ai écrit un cas de test pour le prouver, ce comportement:
#!/usr/local/bin/python2.6
import time
import signal
from somewhere import get_dbc
class Timeout(Exception):
""" Time Exceded """
def _alarm_handler(*args):
raise Timeout
dbc = get_dbc()
signal.signal(signal.SIGALRM, _alarm_handler)
signal.alarm(1)
try:
print "START: ", time.time()
dbc.execute("SELECT SLEEP(10)")
except Timeout:
print "TIMEOUT!", time.time()'
Les "SÉLECTIONNER" SLEEP(10)" est la simulation d'une requête lente, mais je ne vois le même comportement avec un effectif de requêtes lentes.
Le Résultat:
START: 1254440686.69
TIMEOUT! 1254440696.69
Comme vous pouvez le voir, il dort pendant 10 secondes puis-je obtenir de l'Exception Délai.
Questions:
- Pourquoi ne puis-je pas obtenir le signal jusqu'à ce que après l'exécution des finitions?
- Est-il un autre moyen fiable pour limiter les temps d'exécution requête?
Vous devez vous connecter pour publier un commentaire.
@nosklo est tordu à base de solution est élégant et fonctionnel, mais si vous voulez éviter la dépendance à l'égard tordu, la tâche est encore faisable, e.g:
conn
paramètre est en fait nécessaire pour faire ce travail. Ledo_query
commande doit utiliser pour envoyer le résultat en arrière plutôt que de simplementreturn
ing-il.conn
à la pipe le résultat en arrière, puis que ferait beaucoup de sens. Je suppose que la vraie réponse est "ce qui devrait être modifié pour utiliser cet argument pour faire son utilité clair". C'est pas nécessaire pour qu'il fonctionne, après tout, c'est juste peut être.mysql bibliothèque gère interrompu systèmes d'appels en interne de sorte que vous ne verrez pas les effets secondaires de SIGALRM jusqu'à ce que après l'appel d'API complète (avant de tuer le thread ou processus)
Vous pouvez essayer de patcher MySQL-Python et l'utilisation MYSQL_OPT_READ_TIMEOUT option (ajouté en mysql 5.0.25)
La requête est exécutée par le biais d'une fonction C, qui bloque le Python VM à partir de l'exécution jusqu'à ce qu'elle retourne.
C'est (OMI) a vraiment laid solution, mais il ne travail. Vous pouvez exécuter la requête dans un processus séparé (soit via
fork()
ou lamultitraitement
module). Exécutez la minuterie d'alarme dans votre processus principal, et quand vous la recevez, envoyez unSIGINT
ouSIGKILL
pour le processus de l'enfant. Si vous utilisezmultiprocessing
, vous pouvez utiliser leProcess.terminate()
méthode.Utilisation adbapi. Il permet de faire une db appel asynchrone.
Générique notes
J'ai connu le même problème dernièrement avec plusieurs conditions que j'avais rencontré à:
Nous avions suivants de la classe de mise en page (malheureusement je ne peux pas afficher de réelles sources):
Et créé plusieurs threads pour chaque modèle.
Solution Python 3.2
Dans notre application un modèle = une base de données. J'ai donc créé "de connexion de service" pour chaque modèle (on pourrait exécuter
KILL
en connexion parallèle). Par conséquent, si une instance deFirstDatabaseModel
a été créé, 2 connexion de base de données ont été créés; si 5 instances ont été créées seulement 6 connexions ont été utilisés:Maintenant il nous faut un tueur:
Remarque:
connection.execute
= créer un curseur, d'exécuter, de fermer le curseur.Et faire killer "thread-safe" à l'aide de
threading.Lock
:Et enfin ajouter chronométré méthode d'exécution à l'aide de
threading.Timer
:Le processus d'attente pour le réseau I/O est une source ininterrompue de l'état (UNIX chose, qui n'est liée à Python ou MySQL). Il reçoit le signal après l'appel système finitions (probablement comme
EINTR
code d'erreur, même si je ne suis pas sûr).Je pense qu'il est habituellement fait par un outil externe comme
mkill
qui surveille MySQL pour des requêtes de longue durée et les tue.recv
,select
oupoll
appel reçoit un signal, l'appel système renvoie-EINTR
. Ce qui rend Python accrocher ici est la logique de redémarrage.