Re: Commit Hooks Asynchronous?
From: Trent Nelson <trent_at_snakebite.org>
Date: Fri, 3 Feb 2012 19:51:49 -0500
On Fri, Feb 03, 2012 at 04:11:56PM -0800, Cory Finger wrote:
There's no way of doing that out-of-the-box, as far as I know.
You *can* manually enforce sequential access by blocking from within
I've got a hook framework* that analyzes commits; pre-commits are
The revprop metadata is inherently sequential; r6 depends on r5,
The end result was masses of ugly knee-jerk locking code, which I've
[*]: framework is called 'Enversion' (Enterprise Subversion). I'm in
Regards,
Trent.
Hideous locking code below. Note that this isn't indicative of the
-- def _init_evn(self): self.is_rev_for_empty_repo = self.is_rev and self.rev == 0 self.is_rev_for_first_commit = self.is_rev and self.rev == 1 self.is_txn_for_first_commit = self.is_txn and self.base_rev == 0 self.is_normal_rev_or_txn = ( (self.is_txn and self.base_rev >= 1) or (self.is_rev and self.rev > 1) ) # Test mutually-exclusive invariants. assert ( (self.is_rev_for_empty_repo and ( not self.is_txn_for_first_commit and not self.is_rev_for_first_commit and not self.is_normal_rev_or_txn )) or (self.is_rev_for_first_commit and ( not self.is_rev_for_empty_repo and not self.is_txn_for_first_commit and not self.is_normal_rev_or_txn )) or (self.is_txn_for_first_commit and ( not self.is_rev_for_empty_repo and not self.is_rev_for_first_commit and not self.is_normal_rev_or_txn )) or (self.is_normal_rev_or_txn and ( not self.is_rev_for_empty_repo and not self.is_rev_for_first_commit and not self.is_txn_for_first_commit )) ) rc0 = self.r0_revprop_conf if self.is_rev_for_empty_repo and 'version' not in rc0: rc0.last_rev = 0 rc0.version = ESVN_VERSION version = self._load_evn_revprop_int('version', 1) if version != ESVN_VERSION: self.die(e.VersionMismatch % (ESVN_VERSION, version)) if version == 1: self._init_evn_v1() else: raise UnexpectedCodePath def _init_evn_v1(self): if self.is_rev_for_empty_repo: return if self.is_txn_for_first_commit or self.is_rev_for_first_commit: assert self.base_rev == 0 k = Dict() if self.is_txn: k.base_rev = 0 else: k.rev = 1 c = self.rconf(**k) self.__roots = (c, {}) return assert self.base_rev > 0 if self.is_rev: assert self.rev >= 2 else: assert self.base_rev >= 1 max_revlock_waits = self.conf.get('general', 'max-revlock-waits') # Quick sanity check of last_rev to make sure it's not higher than the # highest rev of the repository. self._reload_last_rev() highest_rev = svn.fs.youngest_rev(self.fs, self.pool) if self.last_rev > highest_rev: self.die(e.LastRevTooHigh % (self.last_rev, highest_rev)) if self.is_rev and isinstance(self, RepositoryHook): with open(self.rev_lockfile, 'w') as f: f.write(str(os.getpid())) f.flush() f.close() if self.good_last_rev and self.good_base_rev_roots: self._last_rev_and_base_rev_roots_are_good() return dbg = self.log.debug a = (str(self.rev_or_txn), self.base_rev, self.last_rev) dbg('rev_or_txn: %s, base_rev: %d, last_rev: %d' % a) found = False fn = self.base_rev_lockfile count = itertools.count() while True: c = count.next() dbg('looking for revlock file: %s (attempt: %d)' % (fn, c)) if os.path.isfile(fn): found = True dbg('found revlock after %d attempts' % c) break time.sleep(1) if c == max_revlock_waits: dbg('no revlock found after %d attempts' % c) break if self.good_last_rev and self.good_base_rev_roots: self._last_rev_and_base_rev_roots_are_good() return if found: s = None try: s = open(fn, 'r').read() except: pass if not s: dbg('failed to open/read lock file %s' % fn) else: try: pid = int(s) dbg("pid for lock file %s: %d" % (fn, pid)) except: dbg("invalid pid for lock file %s: %s" % (fn, s)) else: count = itertools.count() still_running = pid_exists(pid) while still_running: a = (count.next(), pid) dbg("[%d] pid %d is still running" % a) time.sleep(1) still_running = pid_exists(pid) good_last_rev = self.good_last_rev good_base_rev_roots = self.good_base_rev_roots if good_last_rev and good_base_rev_roots: self._last_rev_and_base_rev_roots_are_good() return if not good_last_rev: self._last_rev_is_bad() if not good_base_rev_roots: self._base_rev_roots_is_bad() raise UnexpectedCodePath @property def good_last_rev(self): self._reload_last_rev() if isinstance(self, RepositoryHook): return bool(self.last_rev == self.base_rev) else: assert self.is_rev return bool(self.last_rev >= self.base_rev) @property def good_base_rev_roots(self): brc = self.base_revprop_conf brc._reload() return bool(isinstance(brc.roots, Roots)) def _last_rev_is_bad(self): if self.is_rev: a = (self.rev, self.base_rev, self.last_rev, self.latest_rev) if isinstance(self, RepositoryHook): m = e.LastRevNotSetToBaseRevDuringPostCommit else: m = e.OutOfOrderRevisionProcessingAttempt else: assert self.is_txn a = (self.base_rev, self.last_rev, self.latest_rev) if self.base_rev < self.last_rev: m = e.StaleTxnProbablyDueToHighLoad else: m = e.RepositoryOutOfSyncTxn self.die(m % a) def _base_rev_roots_is_bad(self): if self.is_rev: a = (self.rev, self.base_rev, self.last_rev, self.latest_rev) if isinstance(self, RepositoryHook): m = e.RootsMissingFromBaseRevDuringPostCommit else: m = e.InvalidRootsForRev self.die(m % a) else: assert self.is_txn a = (self.txn_name, self.base_rev, self.last_rev, self.latest_rev) m = e.RootsMissingFromBaseRevTxn self.die(m % a) def _last_rev_and_base_rev_roots_are_good(self): try: os.unlink(self.base_rev_lockfile) except: pass brc = self.base_revprop_conf assert isinstance(brc.roots, Roots) if self.is_txn: self.__roots = (brc, brc.roots) return assert self.is_rev rc = self.revprop_conf # XXX force override for now. self.__roots = (rc, self._inherit_roots(brc.roots)) return if rc.roots is not None: # We must be re-processing an already-processed revision. assert isinstance(rc.roots, Roots) self.__roots = (rc, dict(rc.roots)) else: # Processing a revision for the first time outside the context # of a post-commit hook, i.e. repo is being analysed. As with # the post-commit processing logic above, we bring over a sim- # plified version of the base_rev's roots via _inherit_roots. self.__roots = (rc, self._inherit_roots(brc.roots))Received on 2012-02-04 01:52:24 CET |
This is an archived mail posted to the Subversion Dev mailing list.
This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.