diff -r 918fd9931cb7 -r d45cde54d464 hooks/integrity.py --- a/hooks/integrity.py Sat Feb 06 08:45:14 2010 +0100 +++ b/hooks/integrity.py Mon Feb 08 11:08:55 2010 +0100 @@ -8,6 +8,8 @@ """ __docformat__ = "restructuredtext en" +from threading import Lock + from cubicweb import ValidationError from cubicweb.schema import RQLConstraint, RQLUniqueConstraint from cubicweb.selectors import implements @@ -22,6 +24,41 @@ DONT_CHECK_RTYPES_ON_DEL = set(('is', 'is_instance_of', 'wf_info_for', 'from_state', 'to_state')) +_UNIQUE_CONSTRAINTS_LOCK = Lock() +_UNIQUE_CONSTRAINTS_HOLDER = None + +def _acquire_unique_cstr_lock(session): + """acquire the _UNIQUE_CONSTRAINTS_LOCK for the session. + + This lock used to avoid potential integrity pb when checking + RQLUniqueConstraint in two different transactions, as explained in + http://intranet.logilab.fr/jpl/ticket/36564 + """ + global _UNIQUE_CONSTRAINTS_HOLDER + asession = session.actual_session() + if _UNIQUE_CONSTRAINTS_HOLDER is asession: + return + _UNIQUE_CONSTRAINTS_LOCK.acquire() + _UNIQUE_CONSTRAINTS_HOLDER = asession + # register operation responsible to release the lock on commit/rollback + _ReleaseUniqueConstraintsOperation(asession) + +def _release_unique_cstr_lock(session): + global _UNIQUE_CONSTRAINTS_HOLDER + if _UNIQUE_CONSTRAINTS_HOLDER is session: + _UNIQUE_CONSTRAINTS_HOLDER = None + _UNIQUE_CONSTRAINTS_LOCK.release() + else: + assert _UNIQUE_CONSTRAINTS_HOLDER is None + +class _ReleaseUniqueConstraintsOperation(hook.Operation): + def commit_event(self): + pass + def postcommit_event(self): + _release_unique_cstr_lock(self.session) + def rollback_event(self): + _release_unique_cstr_lock(self.session) + class _CheckRequiredRelationOperation(hook.LateOperation): """checking relation cardinality has to be done after commit in @@ -126,6 +163,12 @@ if self.session.deleted_in_transaction(eidto): return for constraint in self.constraints: + # XXX + # * lock RQLConstraint as well? + # * use a constraint id to use per constraint lock and avoid + # unnecessary commit serialization ? + if isinstance(constraint, RQLUniqueConstraint): + _acquire_unique_cstr_lock(self.session) try: constraint.repo_check(self.session, eidfrom, rtype, eidto) except NotImplementedError: