stablerange: add a new 'firstmerge' cache
authorPierre-Yves David <pierre-yves.david@octobus.net>
Wed, 20 Dec 2017 20:46:10 +0100
changeset 3332 7d4c157b6519
parent 3331 846bdf081871
child 3333 96d1cf475c19
stablerange: add a new 'firstmerge' cache This cache store the first merge ancestors of a changesets. This is useful to avoid iteration over the changelog when building stablerange.
hgext3rd/evolve/depthcache.py
hgext3rd/evolve/firstmergecache.py
hgext3rd/evolve/stablerange.py
tests/test-discovery-obshashrange.t
--- a/hgext3rd/evolve/depthcache.py	Wed Dec 20 20:17:11 2017 +0100
+++ b/hgext3rd/evolve/depthcache.py	Wed Dec 20 20:46:10 2017 +0100
@@ -71,7 +71,7 @@
 
         @localrepo.unfilteredmethod
         def destroyed(self):
-            if 'obsstore' in vars(self):
+            if 'depthcache' in vars(self):
                 self.depthcache.clear()
             super(depthcacherepo, self).destroyed()
 
@@ -97,19 +97,13 @@
                     if repo is None:
                         return
                     repo = repo.unfiltered()
-                    # As pointed in 'obscache.update', we could have the changelog
-                    # and the obsstore in charge of updating the cache when new
-                    # items goes it. The tranaction logic would then only be
-                    # involved for the 'pending' and final writing on disk.
-                    self.obsstore.obscache.update(repo)
-                    self.obsstore.obscache.save(repo)
 
                 if (repo.ui.configbool('experimental', 'obshashrange',
                                        False)
                         and repo.ui.configbool('experimental',
                                                'obshashrange.warm-cache',
                                                True)):
-                    tr.addpostclose('warmcache-depthcache', _warmcache)
+                    tr.addpostclose('warmcache-00depthcache', _warmcache)
                 return tr
 
     repo.__class__ = depthcacherepo
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext3rd/evolve/firstmergecache.py	Wed Dec 20 20:46:10 2017 +0100
@@ -0,0 +1,165 @@
+from __future__ import absolute_import
+
+import array
+import weakref
+
+from mercurial import (
+    localrepo,
+    node as nodemod,
+    util,
+)
+
+from . import (
+    error,
+    exthelper,
+    genericcaches,
+    utility,
+)
+
+filterparents = utility.filterparents
+
+eh = exthelper.exthelper()
+
+@eh.reposetup
+def setupcache(ui, repo):
+
+    class firstmergecacherepo(repo.__class__):
+
+        @localrepo.unfilteredpropertycache
+        def firstmergecache(self):
+            cache = firstmergecache()
+            cache.update(self)
+            return cache
+
+        @localrepo.unfilteredmethod
+        def destroyed(self):
+            if 'firstmergecach' in vars(self):
+                self.firstmergecache.clear()
+            super(firstmergecacherepo, self).destroyed()
+
+        if util.safehasattr(repo, 'updatecaches'):
+            @localrepo.unfilteredmethod
+            def updatecaches(self, tr=None):
+                if (repo.ui.configbool('experimental', 'obshashrange',
+                                       False)
+                        and repo.ui.configbool('experimental',
+                                               'obshashrange.warm-cache',
+                                               True)):
+                    self.firstmergecache.update(repo)
+                    self.firstmergecache.save(repo)
+                super(firstmergecacherepo, self).updatecaches(tr)
+
+        else:
+            def transaction(self, *args, **kwargs):
+                tr = super(firstmergecacherepo, self).transaction(*args, **kwargs)
+                reporef = weakref.ref(self)
+
+                def _warmcache(tr):
+                    repo = reporef()
+                    if repo is None:
+                        return
+                    repo = repo.unfiltered()
+                    self.firstmergecache.update(repo)
+                    self.firstmergecache.save(repo)
+
+                if (repo.ui.configbool('experimental', 'obshashrange',
+                                       False)
+                        and repo.ui.configbool('experimental',
+                                               'obshashrange.warm-cache',
+                                               True)):
+                    tr.addpostclose('warmcache-01-firstparentcache', _warmcache)
+                return tr
+
+    repo.__class__ = firstmergecacherepo
+
+class firstmergecache(genericcaches.changelogsourcebase):
+
+    _filepath = 'evoext-firstmerge-00'
+    _cachename = 'evo-ext-firstmerge'
+
+    def __init__(self):
+        super(firstmergecache, self).__init__()
+        self._data = array.array('l')
+
+    def get(self, rev):
+        if len(self._data) <= rev:
+            raise error.ProgrammingError('firstmergecache must be warmed before use')
+        return self._data[rev]
+
+    def _updatefrom(self, repo, data):
+        """compute the rev of one revision, assert previous revision has an hot cache
+        """
+        cl = repo.unfiltered().changelog
+        total = len(data)
+
+        def progress(pos, rev):
+            repo.ui.progress('updating firstmerge cache',
+                             pos, 'rev %s' % rev, unit='revision', total=total)
+        progress(0, '')
+        for idx, rev in enumerate(data, 1):
+            assert rev == len(self._data), (rev, len(self._data))
+            self._data.append(self._firstmerge(cl, rev))
+            if not (idx % 10000): # progress as a too high performance impact
+                progress(idx, rev)
+        progress(None, '')
+
+    def _firstmerge(self, changelog, rev):
+        cl = changelog
+        ps = filterparents(cl.parentrevs(rev))
+        if not ps:
+            return nodemod.nullrev
+        elif len(ps) == 1:
+            # linear commit case
+            return self.get(ps[0])
+        else:
+            return rev
+
+    # cache internal logic
+
+    def clear(self, reset=False):
+        """invalidate the cache content
+
+        if 'reset' is passed, we detected a strip and the cache will have to be
+        recomputed.
+
+        Subclasses MUST overide this method to actually affect the cache data.
+        """
+        super(firstmergecache, self).clear()
+        self._data = array.array('l')
+
+    # crude version of a cache, to show the kind of information we have to store
+
+    def load(self, repo):
+        """load data from disk"""
+        assert repo.filtername is None
+
+        if util.safehasattr(repo, 'cachevfs'):
+            data = repo.cachevfs.tryread(self._filepath)
+        else:
+            data = repo.vfs.tryread('cache/' + self._filepath)
+        self._data = array.array('l')
+        if not data:
+            self._cachekey = self.emptykey
+        else:
+            headerdata = data[:self._cachekeysize]
+            self._cachekey = self._deserializecachekey(headerdata)
+            self._data.fromstring(data[self._cachekeysize:])
+        self._ondiskkey = self._cachekey
+
+    def save(self, repo):
+        """save the data to disk
+
+        Format is pretty simple, we serialise the cache key and then drop the
+        bytearray.
+        """
+        if self._cachekey is None or self._cachekey == self._ondiskkey:
+            return
+
+        if util.safehasattr(repo, 'cachevfs'):
+            cachefile = repo.cachevfs(self._filepath, 'w', atomictemp=True)
+        else:
+            cachefile = repo.vfs('cache/' + self._filepath, 'w', atomictemp=True)
+        headerdata = self._serializecachekey()
+        cachefile.write(headerdata)
+        cachefile.write(self._data.tostring())
+        cachefile.close()
--- a/hgext3rd/evolve/stablerange.py	Wed Dec 20 20:17:11 2017 +0100
+++ b/hgext3rd/evolve/stablerange.py	Wed Dec 20 20:46:10 2017 +0100
@@ -25,8 +25,9 @@
 from mercurial.i18n import _
 
 from . import (
+    exthelper,
+    firstmergecache,
     stablesort,
-    exthelper,
     utility,
 )
 
@@ -34,6 +35,7 @@
 
 eh = exthelper.exthelper()
 eh.merge(stablesort.eh)
+eh.merge(firstmergecache.eh)
 
 # prior to hg-4.2 there are not util.timer
 if util.safehasattr(util, 'timer'):
--- a/tests/test-discovery-obshashrange.t	Wed Dec 20 20:17:11 2017 +0100
+++ b/tests/test-discovery-obshashrange.t	Wed Dec 20 20:46:10 2017 +0100
@@ -38,6 +38,8 @@
   * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-depthcache cache reset (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (8r, 0o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-firstmerge cache reset (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-firstmerge in *.???? seconds (8r) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated served branch cache in *.???? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> wrote served branch cache with 1 labels and 1 nodes (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (8r, 0o) (glob)
@@ -161,6 +163,8 @@
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-depthcache in *.???? seconds (5r) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated stablerange cache in *.???? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (5r, 3o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> strip detected, evo-ext-firstmerge cache reset (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-firstmerge in *.???? seconds (5r) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated base branch cache in *.???? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> wrote base branch cache with 1 labels and 1 nodes (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obscache in *.???? seconds (5r, 3o) (glob)
@@ -243,6 +247,7 @@
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-depthcache in *.???? seconds (1r) (glob)
   * @0000000000000000000000000000000000000000 (*)> updated stablerange cache in *.???? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 1o) (glob)
+  * @0000000000000000000000000000000000000000 (*)> updated evo-ext-firstmerge in *.???? seconds (1r) (glob)
   * @0000000000000000000000000000000000000000 (*)> obscache is out of date, falling back to slower obsstore version (glob)
   * @0000000000000000000000000000000000000000 (*)> updated served branch cache in *.???? seconds (glob)
   * @0000000000000000000000000000000000000000 (*)> wrote served branch cache with 1 labels and 2 nodes (glob)
@@ -305,6 +310,7 @@
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> commit -m foo (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (1r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 0o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-firstmerge in *.???? seconds (1r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obscache is out of date, falling back to slower obsstore version (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated served branch cache in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> wrote served branch cache with 1 labels and 1 nodes (glob)
@@ -422,6 +428,7 @@
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated stablerange cache in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> obshashcache reset - new markers affect cached ranges (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (2r, 3o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-firstmerge in *.???? seconds (2r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated served branch cache in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> wrote served branch cache with 1 labels and 2 nodes (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (2r, 3o) (glob)
@@ -568,12 +575,15 @@
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (1r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated stablerange cache in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 1o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-firstmerge in *.???? seconds (1r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated served branch cache in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> wrote served branch cache with 1 labels and 2 nodes (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (1r, 1o) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> 1 incoming changes - new heads: 4de32a90b66c (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull exited 0 after *.?? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> rollback (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-firstmerge cache reset (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-firstmerge in *.???? seconds (8r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> strip detected, evo-ext-depthcache cache reset (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (8r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated base branch cache in *.???? seconds (glob)
@@ -623,6 +633,7 @@
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-depthcache in *.???? seconds (1r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated stablerange cache in *.???? seconds (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 1o) (glob)
+  * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-firstmerge in *.???? seconds (1r) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> updated evo-ext-obscache in *.???? seconds (1r, 1o) (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> 1 incoming changes - new heads: 4de32a90b66c (glob)
   * @45f8b879de922f6a6e620ba04205730335b6fc7e (*)> pull exited 0 after *.?? seconds (glob)
@@ -736,11 +747,14 @@
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-depthcache cache reset (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-depthcache in *.???? seconds (5r) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obshashrange in *.???? seconds (5r, 11o) (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-firstmerge cache reset (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-firstmerge in *.???? seconds (5r) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> strip detected, evo-ext-obscache cache reset (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obscache in *.???? seconds (5r, 11o) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-depthcache in *.???? seconds (3r) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated stablerange cache in *.???? seconds (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obshashrange in *.???? seconds (3r, 0o) (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-firstmerge in *.???? seconds (3r) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obscache in *.???? seconds (3r, 0o) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated base branch cache in *.???? seconds (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> wrote base branch cache with 1 labels and 1 nodes (glob)
@@ -755,6 +769,7 @@
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated stablerange cache in *.???? seconds (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> obshashcache reset - new markers affect cached ranges (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obshashrange in *.???? seconds (1r, 2o) (glob)
+  * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-firstmerge in *.???? seconds (1r) (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated base branch cache in *.???? seconds (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> wrote base branch cache with 1 labels and 2 nodes (glob)
   * @bebd167eb94d257ace0e814aeb98e6972ed2970d (*)> updated evo-ext-obscache in *.???? seconds (1r, 2o) (glob)