"""Database access helpers"""from twisted.enterprise import adbapifrom twisted.internet import deferfrom twisted.python.failure import Failureimport configimport _mysql_exceptionsfrom twisted.python import logclass DbAccess(object): """ Database access module, that wraps ConnectionPool in singleton pattern and offers it as decorator """ _instance=None _retryCount=0 def __init__(self): """ Not to be called manually, but through instance method """ self.pool=adbapi.ConnectionPool(config.dbapi, host=config.dbhost, db=config.dbname, user=config.dbuser, passwd=config.dbpass, ) @classmethod def instance(klass): """ Obtain a DbAccess instance / signleton pattern """ if klass._instance is None: klass._instance=klass() return klass._instance @classmethod def newinstance(klass): """ Create a new instance and enable it as signleton """ klass._instance=None return klass.instance() @classmethod def run(klass,f,*args,**kwargs): """ A decorator function that runs function from adbapi's runInteraction encodes all type 'unicode' objects passed as arguments to the function into 'str' Example: @dba.run def doStuff(cursor,...): cursor.execute(....) @return defer.Deferred """ pool=klass.instance().pool def _run(*args,**kwargs): for k,v in kwargs.items(): if type(v)==unicode: kwargs[k]=v.encode('utf-8') nargs=[] for x in args: if type(x)==unicode: nargs.append(x.encode('utf-8')) else: nargs.append(x) d=pool.runInteraction(f,*nargs,**kwargs) assert isinstance(d,defer.Deferred) return d.addErrback(klass._handleError,f,*nargs,**kwargs) return _run @classmethod def _handleError(klass,failure,f,*args,**kwargs): """ Handle OperationalError,2006 - MySQLd has gone away Reconnect to server and retry 3 times """ assert isinstance(failure,Failure) failure.trap(_mysql_exceptions.OperationalError) e=failure.value assert isinstance(e,_mysql_exceptions.OperationalError) code=e.args[0] if code==2006: # Server gone away - idle connection dropped, retry log.msg("Mysql gone away, reconnecting",system="DbAccess") if klass._retryCount<3: klass._retryCount+=1 klass.newinstance() return klass.run(f)(*args,**kwargs).addCallback(klass._resetRetryCount) else: log.msg("Giving up retries",system="DbAccess") raise e @classmethod def _resetRetryCount(klass,result): """ Reset retry count for OperationalError/2006 handling """ klass._retryCount=0 return result