server/pool.py
changeset 2835 04034421b072
parent 2765 5e2525d7b1b1
child 3720 5376aaadd16b
equal deleted inserted replaced
2834:7df3494ae657 2835:04034421b072
     1 """CubicWeb server connections pool :
     1 """CubicWeb server connections pool : the repository has a limited number of
     2 
     2 connections pools, each of them dealing with a set of connections on each source
     3 * the rql repository has a limited number of connections pools, each of them
     3 used by the repository. A connections pools (`ConnectionsPool`) is an
     4   dealing with a set of connections on each source used by the repository
     4 abstraction for a group of connection to each source.
     5 
       
     6 * operation may be registered by hooks during a transaction, which will  be
       
     7   fired when the pool is commited or rollbacked
       
     8 
       
     9 This module defined the `ConnectionsPool` class and a set of abstract classes
       
    10 for operation.
       
    11 
     5 
    12 
     6 
    13 :organization: Logilab
     7 :organization: Logilab
    14 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
     8 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
    15 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     9 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
   127 
   121 
   128     def reset_connection(self, source, cnx):
   122     def reset_connection(self, source, cnx):
   129         self.source_cnxs[source.uri] = (source, cnx)
   123         self.source_cnxs[source.uri] = (source, cnx)
   130         self._cursors.pop(source.uri, None)
   124         self._cursors.pop(source.uri, None)
   131 
   125 
   132 
   126 from cubicweb.server.hook import (Operation, LateOperation, SingleOperation,
   133 class Operation(object):
   127                                   SingleLastOperation)
   134     """an operation is triggered on connections pool events related to
   128 from logilab.common.deprecation import class_moved, class_renamed
   135     commit / rollback transations. Possible events are:
   129 Operation = class_moved(Operation)
   136 
   130 PreCommitOperation = class_renamed('PreCommitOperation', Operation)
   137     precommit:
   131 LateOperation = class_moved(LateOperation)
   138       the pool is preparing to commit. You shouldn't do anything things which
   132 SingleOperation = class_moved(SingleOperation)
   139       has to be reverted if the commit fail at this point, but you can freely
   133 SingleLastOperation = class_moved(SingleLastOperation)
   140       do any heavy computation or raise an exception if the commit can't go.
       
   141       You can add some new operation during this phase but their precommit
       
   142       event won't be triggered
       
   143 
       
   144     commit:
       
   145       the pool is preparing to commit. You should avoid to do to expensive
       
   146       stuff or something that may cause an exception in this event
       
   147 
       
   148     revertcommit:
       
   149       if an operation failed while commited, this event is triggered for
       
   150       all operations which had their commit event already to let them
       
   151       revert things (including the operation which made fail the commit)
       
   152 
       
   153     rollback:
       
   154       the transaction has been either rollbacked either
       
   155       * intentionaly
       
   156       * a precommit event failed, all operations are rollbacked
       
   157       * a commit event failed, all operations which are not been triggered for
       
   158         commit are rollbacked
       
   159 
       
   160     order of operations may be important, and is controlled according to:
       
   161     * operation's class
       
   162     """
       
   163 
       
   164     def __init__(self, session, **kwargs):
       
   165         self.session = session
       
   166         self.user = session.user
       
   167         self.repo = session.repo
       
   168         self.schema = session.repo.schema
       
   169         self.config = session.repo.config
       
   170         self.__dict__.update(kwargs)
       
   171         self.register(session)
       
   172         # execution information
       
   173         self.processed = None # 'precommit', 'commit'
       
   174         self.failed = False
       
   175 
       
   176     def register(self, session):
       
   177         session.add_operation(self, self.insert_index())
       
   178 
       
   179     def insert_index(self):
       
   180         """return the index of  the lastest instance which is not a
       
   181         LateOperation instance
       
   182         """
       
   183         for i, op in enumerate(self.session.pending_operations):
       
   184             if isinstance(op, (LateOperation, SingleLastOperation)):
       
   185                 return i
       
   186         return None
       
   187 
       
   188     def handle_event(self, event):
       
   189         """delegate event handling to the opertaion"""
       
   190         getattr(self, event)()
       
   191 
       
   192     def precommit_event(self):
       
   193         """the observed connections pool is preparing a commit"""
       
   194 
       
   195     def revertprecommit_event(self):
       
   196         """an error went when pre-commiting this operation or a later one
       
   197 
       
   198         should revert pre-commit's changes but take care, they may have not
       
   199         been all considered if it's this operation which failed
       
   200         """
       
   201 
       
   202     def commit_event(self):
       
   203         """the observed connections pool is commiting"""
       
   204         raise NotImplementedError()
       
   205 
       
   206     def revertcommit_event(self):
       
   207         """an error went when commiting this operation or a later one
       
   208 
       
   209         should revert commit's changes but take care, they may have not
       
   210         been all considered if it's this operation which failed
       
   211         """
       
   212 
       
   213     def rollback_event(self):
       
   214         """the observed connections pool has been rollbacked
       
   215 
       
   216         do nothing by default, the operation will just be removed from the pool
       
   217         operation list
       
   218         """
       
   219 
       
   220 
       
   221 class PreCommitOperation(Operation):
       
   222     """base class for operation only defining a precommit operation
       
   223     """
       
   224 
       
   225     def precommit_event(self):
       
   226         """the observed connections pool is preparing a commit"""
       
   227         raise NotImplementedError()
       
   228 
       
   229     def commit_event(self):
       
   230         """the observed connections pool is commiting"""
       
   231 
       
   232 
       
   233 class LateOperation(Operation):
       
   234     """special operation which should be called after all possible (ie non late)
       
   235     operations
       
   236     """
       
   237     def insert_index(self):
       
   238         """return the index of  the lastest instance which is not a
       
   239         SingleLastOperation instance
       
   240         """
       
   241         for i, op in enumerate(self.session.pending_operations):
       
   242             if isinstance(op, SingleLastOperation):
       
   243                 return i
       
   244         return None
       
   245 
       
   246 
       
   247 class SingleOperation(Operation):
       
   248     """special operation which should be called once"""
       
   249     def register(self, session):
       
   250         """override register to handle cases where this operation has already
       
   251         been added
       
   252         """
       
   253         operations = session.pending_operations
       
   254         index = self.equivalent_index(operations)
       
   255         if index is not None:
       
   256             equivalent = operations.pop(index)
       
   257         else:
       
   258             equivalent = None
       
   259         session.add_operation(self, self.insert_index())
       
   260         return equivalent
       
   261 
       
   262     def equivalent_index(self, operations):
       
   263         """return the index of the equivalent operation if any"""
       
   264         equivalents = [i for i, op in enumerate(operations)
       
   265                        if op.__class__ is self.__class__]
       
   266         if equivalents:
       
   267             return equivalents[0]
       
   268         return None
       
   269 
       
   270 
       
   271 class SingleLastOperation(SingleOperation):
       
   272     """special operation which should be called once and after all other
       
   273     operations
       
   274     """
       
   275     def insert_index(self):
       
   276         return None
       
   277 
       
   278 from logging import getLogger
       
   279 from cubicweb import set_log_methods
       
   280 set_log_methods(Operation, getLogger('cubicweb.session'))