Ejegg has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/197205

Change subject: Add execute_timeout to limit query lifespan
......................................................................

Add execute_timeout to limit query lifespan

Brutally murders database connections after a set interval, raising
an OperationalError on the calling thread.  The Connection instance
is useless afterward and should be re-instantiated.

Change-Id: I0fca9533bb6c1e2bbf3cd88b4e298ac7e3d07335
---
M database/db.py
1 file changed, 27 insertions(+), 1 deletion(-)


  git pull ssh://gerrit.wikimedia.org:29418/wikimedia/fundraising/tools 
refs/changes/05/197205/1

diff --git a/database/db.py b/database/db.py
index 4265627..4bed155 100644
--- a/database/db.py
+++ b/database/db.py
@@ -4,6 +4,7 @@
 import MySQLdb as Dbi
 import atexit
 import os
+import threading
 
 from signal import signal, SIGTERM, SIG_DFL
 from process.logging import Logger as log
@@ -11,8 +12,10 @@
 
 class Connection(object):
     def __init__(self, debug=False, **kw):
+        self.connectionArgs = kw
         self.db_conn = Dbi.connect(**kw)
         self.debug = debug
+        self.connection_id = None
 
     def close(self):
         self.db_conn.commit()
@@ -37,6 +40,30 @@
         out = cursor.fetchall()
         cursor.close()
         return out
+
+    def execute_timeout(self,  sql,  params=None,  timeout=0):
+        """ Execute a query with timeout.  When a timeout occurs, kill the 
database connection """
+        if timeout == 0:
+            return self.execute(sql,  params)
+
+        if self.connection_id == None:
+            self.connection_id = self.execute('SELECT CONNECTION_ID() AS 
cid')[0]['cid']
+
+        deathClock = threading.Timer(timeout,  self.kill_connection)
+        deathClock.start()
+
+        try:
+            return self.execute(sql,  params)
+        finally:
+            deathClock.cancel()
+
+    def kill_connection(self):
+        if self.debug:
+            log.debug('Killing connection {}'.format(self.connection_id))
+        killerConnection = Dbi.connect(**self.connectionArgs)
+        cursor = killerConnection.cursor()
+        cursor.execute('KILL CONNECTION {}'.format(self.connection_id))
+        killerConnection.close()
 
     def execute_paged(self, query, pageIndex, pageSize = 1000, dir = 'ASC'):
         """ Execute a paged query. This will yield a dictionary of the results
@@ -117,7 +144,6 @@
         for k, s in self.params.items():
             qparams[k] = "'%s'" % s
         return self.uninterpolated_sql() % qparams
-
 
 db_conn = dict()
 

-- 
To view, visit https://gerrit.wikimedia.org/r/197205
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I0fca9533bb6c1e2bbf3cd88b4e298ac7e3d07335
Gerrit-PatchSet: 1
Gerrit-Project: wikimedia/fundraising/tools
Gerrit-Branch: master
Gerrit-Owner: Ejegg <eeggles...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to