[doc] Renaming of part II sub-sections accordingly.
"""CubicWeb server connections pool :* the rql repository has a limited number of connections pools, each of them dealing with a set of connections on each source used by the repository* operation may be registered by hooks during a transaction, which will be fired when the pool is commited or rollbackedThis module defined the `ConnectionsPool` class and a set of abstract classesfor operation.:organization: Logilab:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr"""__docformat__="restructuredtext en"importsysclassConnectionsPool(object):"""handle connections on a set of sources, at some point associated to a user session """def__init__(self,sources):# dictionnary of (source, connection), indexed by sources'uriself.source_cnxs={}forsourceinsources:self.source_cnxs[source.uri]=(source,source.get_connection())ifnot'system'inself.source_cnxs:self.source_cnxs['system']=self.source_cnxs[sources[0].uri]self._cursors={}defcommit(self):"""commit the current transaction for this user"""# FIXME: what happends if a commit fail# would need a two phases commit or like, but I don't know how to do# this using the db-api...forsource,cnxinself.source_cnxs.values():# let exception propagatescnx.commit()defrollback(self):"""rollback the current transaction for this user"""forsource,cnxinself.source_cnxs.values():# catch exceptions, rollback other sources anywaytry:cnx.rollback()except:source.critical('rollback error',exc_info=sys.exc_info())defclose(self,i_know_what_i_do=False):"""close all connections in the pool"""ifi_know_what_i_doisnotTrue:# unexpected closing safety beltraiseRuntimeError('pool shouldn\'t be closed')forcuinself._cursors.values():try:cu.close()except:continuefor_,cnxinself.source_cnxs.values():try:cnx.close()except:continue# internals ###############################################################defpool_set(self,session):"""pool is being set"""self.check_connections()defpool_reset(self,session):"""pool is being reseted"""forsource,cnxinself.source_cnxs.values():source.pool_reset(cnx)def__getitem__(self,uri):"""subscription notation provide access to sources'cursors"""try:cursor=self._cursors[uri]exceptKeyError:cursor=self.source_cnxs[uri][1].cursor()ifcursorisnotNone:# None possible on sources without cursor support such as ldapself._cursors[uri]=cursorreturncursordefsources(self):"""return the source objects handled by this pool"""# implementation details of flying insert requires the system source# firstyieldself.source_cnxs['system']foruri,(source,cursor)inself.source_cnxs.items():ifuri=='system':continueyieldsource#return [source_cnx[0] for source_cnx in self.source_cnxs.values()]defsource(self,uid):"""return the source object with the given uri"""returnself.source_cnxs[uid][0]defconnection(self,uid):"""return the connection on the source object with the given uri"""returnself.source_cnxs[uid][1]defreconnect(self,source):"""reopen a connection for this source"""source.info('trying to reconnect')self.source_cnxs[source.uri]=(source,source.get_connection())delself._cursors[source.uri]defcheck_connections(self):forsource,cnxinself.source_cnxs.itervalues():newcnx=source.check_connection(cnx)ifnewcnxisnotNone:self.reset_connection(source,newcnx)defreset_connection(self,source,cnx):self.source_cnxs[source.uri]=(source,cnx)self._cursors.pop(source.uri,None)classOperation(object):"""an operation is triggered on connections pool events related to commit / rollback transations. Possible events are: precommit: the pool is preparing to commit. You shouldn't do anything things which has to be reverted if the commit fail at this point, but you can freely do any heavy computation or raise an exception if the commit can't go. You can add some new operation during this phase but their precommit event won't be triggered commit: the pool is preparing to commit. You should avoid to do to expensive stuff or something that may cause an exception in this event revertcommit: if an operation failed while commited, this event is triggered for all operations which had their commit event already to let them revert things (including the operation which made fail the commit) rollback: the transaction has been either rollbacked either * intentionaly * a precommit event failed, all operations are rollbacked * a commit event failed, all operations which are not been triggered for commit are rollbacked order of operations may be important, and is controlled according to: * operation's class """def__init__(self,session,**kwargs):self.session=sessionself.user=session.userself.repo=session.repoself.schema=session.repo.schemaself.config=session.repo.configself.__dict__.update(kwargs)self.register(session)# execution informationself.processed=None# 'precommit', 'commit'self.failed=Falsedefregister(self,session):session.add_operation(self,self.insert_index())definsert_index(self):"""return the index of the lastest instance which is not a LateOperation instance """fori,opinenumerate(self.session.pending_operations):ifisinstance(op,(LateOperation,SingleLastOperation)):returnireturnNonedefhandle_event(self,event):"""delegate event handling to the opertaion"""getattr(self,event)()defprecommit_event(self):"""the observed connections pool is preparing a commit"""defrevertprecommit_event(self):"""an error went when pre-commiting this operation or a later one should revert pre-commit's changes but take care, they may have not been all considered if it's this operation which failed """defcommit_event(self):"""the observed connections pool is commiting"""raiseNotImplementedError()defrevertcommit_event(self):"""an error went when commiting this operation or a later one should revert commit's changes but take care, they may have not been all considered if it's this operation which failed """defrollback_event(self):"""the observed connections pool has been rollbacked do nothing by default, the operation will just be removed from the pool operation list """classPreCommitOperation(Operation):"""base class for operation only defining a precommit operation """defprecommit_event(self):"""the observed connections pool is preparing a commit"""raiseNotImplementedError()defcommit_event(self):"""the observed connections pool is commiting"""classLateOperation(Operation):"""special operation which should be called after all possible (ie non late) operations """definsert_index(self):"""return the index of the lastest instance which is not a SingleLastOperation instance """fori,opinenumerate(self.session.pending_operations):ifisinstance(op,SingleLastOperation):returnireturnNoneclassSingleOperation(Operation):"""special operation which should be called once"""defregister(self,session):"""override register to handle cases where this operation has already been added """operations=session.pending_operationsindex=self.equivalent_index(operations)ifindexisnotNone:equivalent=operations.pop(index)else:equivalent=Nonesession.add_operation(self,self.insert_index())returnequivalentdefequivalent_index(self,operations):"""return the index of the equivalent operation if any"""equivalents=[ifori,opinenumerate(operations)ifop.__class__isself.__class__]ifequivalents:returnequivalents[0]returnNoneclassSingleLastOperation(SingleOperation):"""special operation which should be called once and after all other operations """definsert_index(self):returnNonefromloggingimportgetLoggerfromcubicwebimportset_log_methodsset_log_methods(Operation,getLogger('cubicweb.session'))