Domanda

I am working on a web service with Twisted that is responsible for calling up several packages I had previously used on the command line. The routines these packages handle were being prototyped on their own but now are ready to be integrated into our webservice.

In short, I have several different modules that all create a mysql connection property internally in their original command line forms. Take this for example:

class searcher:
  def __init__(self,lat,lon,radius):
    self.conn = getConnection()[1]
    self.con=self.conn.cursor();

    self.mgo = getConnection(True)

    self.lat = lat
    self.lon = lon
    self.radius = radius
    self.profsinrange()
    self.cache = memcache.Client(["173.220.194.84:11211"])

The getConnection function is just a helper that returns a mongo or mysql cursor respectively. Again, this is all prototypical :)

The problem I am experiencing is when implemented as a consistently running server using Twisted's WSGI resource, the sql connection created in init times out, and subsequent requests don't seem to regenerate it. Example code for small server app:

from twisted.web import server
from twisted.web.wsgi import WSGIResource
from twisted.python.threadpool import ThreadPool
from twisted.internet import reactor
from twisted.application import service, strports
import cgi

import gnengine
import nn

wsgiThreadPool = ThreadPool()
wsgiThreadPool.start()

# ensuring that it will be stopped when the reactor shuts down
reactor.addSystemEventTrigger('after', 'shutdown', wsgiThreadPool.stop)


def application(environ, start_response):
    start_response('200 OK', [('Content-type','text/plain')])
    params = cgi.parse_qs(environ['QUERY_STRING'])
    try:
      lat =  float(params['lat'][0])
      lon = float(params['lon'][0])
      radius = int(params['radius'][0])
      query_terms = params['query']
      s = gnengine.searcher(lat,lon,radius)
      query_terms = ' '.join( query_terms )
      json = s.query(query_terms)
      return [json]
    except Exception, e:
      return [str(e),str(params)]

    return ['error']

wsgiAppAsResource = WSGIResource(reactor, wsgiThreadPool, application)

# Hooks for twistd
application = service.Application('Twisted.web.wsgi Hello World Example')
server = strports.service('tcp:8080', server.Site(wsgiAppAsResource))
server.setServiceParent(application)

The first few requests work fine, but after mysqls wait_timeout expires, the dread error 2006 "Mysql has gone away" error surfaces. It had been my understanding that every request to the WSGI Twisted resource would run the application function, thereby regenerating the searcher object and re-leasing the connection. If this isn't the case, how can I make the requests processed as such? Is this kind of Twisted deployment not transactional in this sense? Thanks!

EDIT: Per request, here is the prototype helper function calling up the connection:

def getConnection(mong = False):
    if mong == False:
    connection = mysql.connect(host = db_host,
                   user = db_user,
                   passwd = db_pass,
                   db = db,
                   cursorclass=mysql.cursors.DictCursor)
    cur = connection.cursor();
    return (cur,connection)
    else:
    return pymongo.Connection('173.220.194.84',27017).gonation_test
È stato utile?

Soluzione

i was developing a piece of software with twisted where i had to utilize a constant MySQL database connection. i did run into this problem and digging through the twisted documentation extensively and posting a few questions i was unable to find a proper solution.There is a boolean parameter you can pass when you are instantiating the adbapi.connectionPool class; however it never seemed to work and i kept getting the error irregardless. However, what i am guessing the reconnect boolean represents is the destruction of the connection object when SQL disconnect does occur.

adbapi.ConnectionPool("MySQLdb", cp_reconnect=True, host="", user="", passwd="", db="")

I have not tested this but i will re-post some results when i do or if anyone else has please share.

When i was developing the script i was using twisted 8.2.0 (i havent touched twisted in a while) and back then the framework had no such explicit keep alive method, so i developed a ping/keepalive extension employing event driven paradigm twisted builds upon in conjunction with direct MySQLdb module ping() method (see code comment). As i was typing this response; however, i did look around the current twisted documentation i was still unable to find an explicit keep-alive method or parameter. My guess is because twisted itself does not have database connectivity libraries/classes. It uses the methods available to python and provides an indirect layer of interfacing with those modules; with some exposure for direct calls to the database library being used. This is accomplished by using the adbapi.runWithConnection method.

here is the module i wrote under twisted 8.2.0 and python 2.6; you can set the intervals between pings. what the script does is, every 20 minutes it pings the database and if it fails, it attempts to reconnect back to it every 60 seconds. I must warn that the script does NOT handle sudden/dropped connection; that you can handle through addErrback whenever you run a query through twisted, atleast thats how i did it. I have noticed that whenever database connection drops, you can only find out if it has when you are executing a query and the event raises an errback, and then at that point you deal with it. Basically, if i dont run a query for 10 minutes, and my database disconnects me, my application will not respond in real time. the application will realize the connection has been dropped when it runs the query that follows; so the database could have disconnected us 1 minute after the first query, 5, 9, etc.... I guess this sort of goes back to the original idea that i have stated, twisted utilizes python's own libraries or 3rd party libraries for database connectivity and because of that, some things are handled a bit differently.



from twisted.enterprise import adbapi
from twisted.internet import reactor, defer, task

class sqlClass:
        def __init__(self, db_pointer):
                self.dbpool=db_pointer
                self.dbping = task.LoopingCall(self.dbping)
                self.dbping.start(1200) #20 minutes = 1200 seconds; i found out that if MySQL socket is idled for 20 minutes or longer, MySQL itself disconnects the session for security reasons; i do believe you can change that in the configuration of the database server itself but it may not be recommended.
                self.reconnect=False
                print "database ping initiated"

        def dbping(self):
                def ping(conn):
                        conn.ping() #what happens here is that twisted allows us to access methods from the MySQLdb module that python posesses; i chose to use the native command instead of sending null commands to the database.
                pingdb=self.dbpool.runWithConnection(ping)
                pingdb.addCallback(self.dbactive)
                pingdb.addErrback(self.dbout)
                print "pinging database"

        def dbactive(self, data):
                if data==None and self.reconnect==True:
                        self.dbping.stop()
                        self.reconnect=False
                        self.dbping.start(1200) #20 minutes = 1200 seconds
                        print "Reconnected to database!"
                elif data==None:
                        print "database is active"

        def dbout(self, deferr):
                #print deferr
                if self.reconnect==False:
                        self.dbreconnect()
                elif self.reconnect==True:
                        print "Unable to reconnect to database"
                print "unable to ping MySQL database!"

        def dbreconnect(self, *data):
                self.dbping.stop()
                self.reconnect=True
                #self.dbping = task.LoopingCall(self.dbping)
                self.dbping.start(60) #60
if __name__ == "__main__":
        db = sqlClass(adbapi.ConnectionPool("MySQLdb", cp_reconnect=True, host="", user="", passwd="", db=""))
        reactor.callLater(2, db.dbping)
        reactor.run()

let me know how it works out for you :)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top