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')) |
|