6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
6 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
7 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
7 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
8 """ |
8 """ |
9 __docformat__ = "restructuredtext en" |
9 __docformat__ = "restructuredtext en" |
10 |
10 |
|
11 from threading import Lock |
11 from datetime import datetime |
12 from datetime import datetime |
12 |
13 |
13 from cubicweb import UnknownProperty, ValidationError, BadConnectionId |
14 from cubicweb import UnknownProperty, ValidationError, BadConnectionId |
14 from cubicweb.schema import RQLConstraint, RQLUniqueConstraint |
15 from cubicweb.schema import RQLConstraint, RQLUniqueConstraint |
15 from cubicweb.server.pool import Operation, LateOperation, PreCommitOperation |
16 from cubicweb.server.pool import Operation, LateOperation, PreCommitOperation |
22 DONT_CHECK_RTYPES_ON_ADD = set(('owned_by', 'created_by', |
23 DONT_CHECK_RTYPES_ON_ADD = set(('owned_by', 'created_by', |
23 'is', 'is_instance_of', |
24 'is', 'is_instance_of', |
24 'wf_info_for', 'from_state', 'to_state')) |
25 'wf_info_for', 'from_state', 'to_state')) |
25 DONT_CHECK_RTYPES_ON_DEL = set(('is', 'is_instance_of', |
26 DONT_CHECK_RTYPES_ON_DEL = set(('is', 'is_instance_of', |
26 'wf_info_for', 'from_state', 'to_state')) |
27 'wf_info_for', 'from_state', 'to_state')) |
|
28 |
|
29 _UNIQUE_CONSTRAINTS_LOCK = Lock() |
|
30 _UNIQUE_CONSTRAINTS_HOLDER = None |
|
31 |
|
32 class _ReleaseUniqueConstraintsHook(Operation): |
|
33 def commit_event(self): |
|
34 pass |
|
35 def postcommit_event(self): |
|
36 _release_unique_cstr_lock(self.session) |
|
37 def rollback_event(self): |
|
38 _release_unique_cstr_lock(self.session) |
|
39 |
|
40 def _acquire_unique_cstr_lock(session): |
|
41 """acquire the _UNIQUE_CONSTRAINTS_LOCK for the session. |
|
42 |
|
43 This lock used to avoid potential integrity pb when checking |
|
44 RQLUniqueConstraint in two different transactions, as explained in |
|
45 http://intranet.logilab.fr/jpl/ticket/36564 |
|
46 """ |
|
47 global _UNIQUE_CONSTRAINTS_HOLDER |
|
48 asession = session.actual_session() |
|
49 if _UNIQUE_CONSTRAINTS_HOLDER is asession: |
|
50 return |
|
51 _UNIQUE_CONSTRAINTS_LOCK.acquire() |
|
52 _UNIQUE_CONSTRAINTS_HOLDER = asession |
|
53 # register operation responsible to release the lock on commit/rollback |
|
54 _ReleaseUniqueConstraintsHook(asession) |
|
55 |
|
56 def _release_unique_cstr_lock(session): |
|
57 global _UNIQUE_CONSTRAINTS_HOLDER |
|
58 if _UNIQUE_CONSTRAINTS_HOLDER is session: |
|
59 _UNIQUE_CONSTRAINTS_HOLDER = None |
|
60 _UNIQUE_CONSTRAINTS_LOCK.release() |
|
61 else: |
|
62 assert _UNIQUE_CONSTRAINTS_HOLDER is None |
27 |
63 |
28 |
64 |
29 def relation_deleted(session, eidfrom, rtype, eidto): |
65 def relation_deleted(session, eidfrom, rtype, eidto): |
30 session.transaction_data.setdefault('pendingrelations', []).append( |
66 session.transaction_data.setdefault('pendingrelations', []).append( |
31 (eidfrom, rtype, eidto)) |
67 (eidfrom, rtype, eidto)) |
214 if eidfrom in pending: |
250 if eidfrom in pending: |
215 return |
251 return |
216 if eidto in pending: |
252 if eidto in pending: |
217 return |
253 return |
218 for constraint in self.constraints: |
254 for constraint in self.constraints: |
|
255 # XXX |
|
256 # * lock RQLConstraint as well? |
|
257 # * use a constraint id to use per constraint lock and avoid |
|
258 # unnecessary commit serialization ? |
|
259 if isinstance(constraint, RQLUniqueConstraint): |
|
260 _acquire_unique_cstr_lock(self.session) |
219 try: |
261 try: |
220 constraint.repo_check(self.session, eidfrom, rtype, eidto) |
262 constraint.repo_check(self.session, eidfrom, rtype, eidto) |
221 except NotImplementedError: |
263 except NotImplementedError: |
222 self.critical('can\'t check constraint %s, not supported', |
264 self.critical('can\'t check constraint %s, not supported', |
223 constraint) |
265 constraint) |